EXTERN_C void sopen_alpha_read(HDRTYPE* hdr) { /* this function will be called by the function SOPEN in "biosig.c" Input: char* Header // contains the file content Output: HDRTYPE *hdr // defines the HDR structure accoring to "biosig.h" */ size_t count; unsigned int k; const char *FileName = hdr->FileName; fprintf(stdout,"Warning: support for alpha format is just experimental.\n"); char* fn = (char*)malloc(strlen(hdr->FileName)+15); strcpy(fn,hdr->FileName); // Flawfinder: ignore const size_t bufsiz = 4096; char buf[bufsiz]; // alpha.alp cal_res digin digvidtc eog marker measure mkdef montage rawdata rawhead report.txt r_info sleep const char *f2 = "alpha.alp"; char *tmpstr = strrchr(fn,FILESEP); if (tmpstr) strcpy(tmpstr+1,f2); // Flawfinder: ignore else strcpy(fn,f2); // Flawfinder: ignore FILE *fid = fopen(fn,"r"); count = fread(buf,1,bufsiz-1,fid); fclose(fid); buf[count]=0; // terminating 0 character char *t = strtok(buf,"\xA\xD"); while (t) { if (VERBOSE_LEVEL>7) fprintf(stdout,"0: %s \n",t); if (!strncmp(t,"Version = ",9)) hdr->VERSION = atof(t+9); else if (!strncmp(t,"Id = ",4)) { strncpy(hdr->ID.Manufacturer._field,t+5,MAX_LENGTH_MANUF); hdr->ID.Manufacturer.Name = hdr->ID.Manufacturer._field; } t = strtok(NULL,"\xA\xD"); } f2 = "rawhead"; if (tmpstr) strcpy(tmpstr+1,f2); // Flawfinder: ignore else strcpy(fn,f2); // Flawfinder: ignore int Notch = 0; int Bits = 0; double DigMax=0, DigMin=0; uint16_t gdftyp = 0; int ns = 0; fid = fopen(fn,"r"); count = fread(buf,1,bufsiz-1,fid); fclose(fid); buf[count]=0; // terminating 0 character t = strtok(buf,"\xA\xD"); char STATUS = 1; uint32_t *ChanOrder=NULL; char **ChanType = NULL; while ((t!=NULL) && (STATUS<9)) { char *t1 = strchr(t,'='); if (VERBOSE_LEVEL>7) fprintf(stdout,"<%6.2f> %i- %s | %s\n",hdr->VERSION, STATUS,t,t1); if (t1) { t1[-1] = 0; t1++; if (STATUS == 1) { if (!strcmp(t,"Version")) hdr->VERSION = atof(t1); else if (!strcmp(t,"BitsPerValue")) { Bits = atoi(t1); switch (Bits) { case 12: gdftyp = 255+12; // hdr->FILE.LittleEndian = 0; DigMax = (1<<(Bits-1))-1; DigMin = -(1<<(Bits-1)); break; case 16: gdftyp = 3; DigMax = 32752.0; //!!! that's the maximum value found in alpha-trace files DigMin = -32736.0; //!!! that's the minimum value found in alpha-trace files break; case 32: gdftyp = 5; break; DigMax = (1<<(Bits-1))-1; DigMin = -(1<<(Bits-1)); } } else if (!strcmp(t,"ChanCount")) { hdr->NS = atoi(t1); hdr->CHANNEL = (CHANNEL_TYPE*)realloc(hdr->CHANNEL, hdr->NS*sizeof(CHANNEL_TYPE)); ChanOrder = (uint32_t*)calloc(hdr->NS,sizeof(uint32_t)*2); ChanType = (char**)calloc(hdr->NS,sizeof(char*)); } else if (!strcmp(t,"SampleFreq")) hdr->SampleRate = atof(t1); else if (!strcmp(t,"SampleCount")) { hdr->NRec = atoi(t1); hdr->SPR = 1; } else if (!strcmp(t,"NotchFreq")) Notch = atof(t1); else if (!strcmp(t,"DispFlags") && (hdr->VERSION < 411.89)) STATUS = 2; else if (!strcmp(t,"Sec2Marker") && (hdr->VERSION > 411.89)) STATUS = 2; } else if (STATUS == 2) { if (ns>=hdr->NS) { ns = 0; STATUS = 3; } else { CHANNEL_TYPE *hc = hdr->CHANNEL+ns; hc->GDFTYP = gdftyp; hc->Notch = Notch; hc->LeadIdCode = 0; hc->SPR = hdr->SPR; //hc->bi8 = GDFTYP_BITS[gdftyp]*ns; hc->DigMax = DigMax; hc->DigMin = DigMin; hc->OnOff = 1; hc->Cal = 1.0; hc->Off = 0.0; hc->PhysMax = hc->DigMax; hc->PhysMin = hc->DigMin; strncpy(hc->Label, t, MAX_LENGTH_LABEL+1); char* t2= strchr(t1,','); t2[0] = 0; while (isspace((++t2)[0])); char* t3= strchr(t2,','); t3[0] = 0; while (isspace((++t3)[0])); char* t4= strchr(t3,','); t4[0] = 0; while (isspace((++t4)[0])); ChanOrder[ns*2] = atoi(t2); ChanOrder[ns*2+1] = ns; ChanType[ns] = t3; ns++; } } else if (STATUS == 3) { // decode information (filters, PhysDim, etc.) and assign to corresponding channels. char *pd = NULL; float tmp1, tmp2, HighPass, LowPass; #if !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L sscanf(t1, "%f, %f, %f, %f, %as", &HighPass,&LowPass, &tmp1,&tmp2, &pd); #else sscanf(t1, "%f, %f, %f, %f, %ms", &HighPass,&LowPass, &tmp1,&tmp2, &pd); #endif strrchr(pd,',')[0]=0; if (!strcmp(pd,"%%")) pd[1]=0; uint16_t pdc = PhysDimCode(pd); if (pd) free(pd); char flag = 0; for (k=0; k < hdr->NS; k++) { if ((ChanType[k]!=NULL) && !strcmp(t,ChanType[k])) { CHANNEL_TYPE *hc = hdr->CHANNEL+k; hc->PhysDimCode = pdc; //strcpy(hc->PhysDim,pd); hc->LowPass = LowPass; hc->HighPass = HighPass; ChanType[k] = NULL; } if (ChanType[k] != NULL) flag = 1; // not done yet } if (!flag) STATUS = 99; // done with channel definition } } t = strtok(NULL,"\xA\xD"); } hdr->AS.bpb8 = (GDFTYP_BITS[gdftyp]*hdr->NS); // hdr->AS.bpb = (GDFTYP_BITS[gdftyp]*hdr->NS)>>3; // do not rely on this, because some bits can get lost // sort channels qsort(ChanOrder,hdr->NS,2*sizeof(uint32_t),&u32cmp); for (k=0; k<hdr->NS; k++) { hdr->CHANNEL[ChanOrder[2*k+1]].bi8 = GDFTYP_BITS[gdftyp]*k; hdr->CHANNEL[ChanOrder[2*k+1]].bi = (GDFTYP_BITS[gdftyp]*k)>>3; } free(ChanOrder); free(ChanType); f2 = "cal_res"; if (tmpstr) strcpy(tmpstr+1,f2); // Flawfinder: ignore else strcpy(fn,f2); // Flawfinder: ignore fid = fopen(fn,"r"); if (fid!=NULL) { if (VERBOSE_LEVEL>7) fprintf(stdout,"cal_res: \n"); count = fread(buf,1,bufsiz-1,fid); fclose(fid); buf[count]=0; // terminating 0 character t = strtok(buf,"\xA\xD"); t = strtok(NULL,"\xA\xD"); // skip lines 1 and 2 /* char label[MAX_LENGTH_LABEL+1]; char flag[MAX_LENGTH_LABEL+1]; double cal,off; */ char *t0,*t1,*t2,*t3; unsigned n=0; // for (k=0; max(k,n)<hdr->NS; k++) { t = strtok(NULL,"\xA\xD"); if (t==NULL) { fprintf(stderr,"Warning SOPEN(alpha): scaling coefficients not defined for all channels\n"); break; } // strncpy(hc->Label,t,min(strcspn(t," =,"),MAX_LENGTH_LABEL)); t0 = strchr(t,'=');t0[0]=0;t0++; int ix = strlen(t)-1; while ((ix>0) && isspace(t[ix])) t[ix--] = 0; // remove trailing spaces t1 = strchr(t0,',');t1[0]=0;t1++; t2 = strchr(t1,',');t2[0]=0;t2++; t3 = strchr(t2,',');t3[0]=0;t3++; n = atoi(t); // n==0 if label is provided, n>0 if channel number is provided /* does not work because ambigous labels are used in rawhead and cal_res (e.g. T3 and T7) if (!n) for (n=0; n<hdr->NS; n++) { if (!strcmp(hdr->CHANNEL[n].Label,t)) { n++; break; } } */ if (VERBOSE_LEVEL>7) fprintf(stdout,"cal_res: %i %i <%s> %s %s %s\n",k,n,t,t1,t2,t3); CHANNEL_TYPE *hc = hdr->CHANNEL + (n>0 ? n-1 : k); // channel can be denoted by label or number hc->Cal = atof(t1); hc->Off = 0; if (VERBOSE_LEVEL>7) fprintf(stdout," <%s> %s = ###, %f, %f\n", t1,hc->Label,hc->Cal,hc->Off); hc->PhysMax = (hc->DigMax - hc->Off) * hc->Cal; hc->PhysMin = (hc->DigMin - hc->Off) * hc->Cal; hc->XYZ[0]=0; hc->XYZ[1]=0; hc->XYZ[2]=0; } } f2 = "r_info"; if (tmpstr) strcpy(tmpstr+1,f2); // Flawfinder: ignore else strcpy(fn,f2); // Flawfinder: ignore fid = fopen(fn,"r"); if (fid!=NULL) { if (VERBOSE_LEVEL>7) fprintf(stdout,"r_info: \n"); count = fread(buf,1,bufsiz-1,fid); fclose(fid); buf[count]=0; // terminating 0 character struct tm T; t = strtok(buf,"\xA\xD"); t = strtok(NULL,"\xA\xD"); // skip line 1 while (t!=NULL) { char *t1 = strchr(t,'='); t1[0] = 0; while (isspace((++t1)[0])); for (k=strlen(t); (k>0) && isspace(t[--k]); t[k]=0); for (k=strlen(t1); (k>0) && isspace(t1[--k]); t1[k]=0); if (VERBOSE_LEVEL>7) fprintf(stdout,"r_info: %s = %s\n",t,t1); if (0) {} else if (!strcmp(t,"RecId")) strncpy(hdr->ID.Recording,t1,MAX_LENGTH_RID); else if (!strcmp(t,"RecDate")) { sscanf(t1,"%02d.%02d.%04d",&T.tm_mday,&T.tm_mon,&T.tm_year); T.tm_year -=1900; T.tm_mon -=1; } else if (!strcmp(t,"RecTime")) sscanf(t1,"%02d.%02d.%02d",&T.tm_hour,&T.tm_min,&T.tm_sec); else if (!strcmp(t,"TechSal")) { if (hdr->ID.Technician) free(hdr->ID.Technician); hdr->ID.Technician = strdup(t1); } else if (!strcmp(t,"TechTitle") || !strcmp(t,"TechLast") || !strcmp(t,"TechFirst")) { size_t l0 = strlen(hdr->ID.Technician); size_t l1 = strlen(t1); hdr->ID.Technician = (char*)realloc(hdr->ID.Technician,l0+l1+2); hdr->ID.Technician[l0] = ' '; strcpy(hdr->ID.Technician+l0+1, t1); // Flawfinder: ignore } t = strtok(NULL,"\xA\xD"); } hdr->T0 = tm_time2gdf_time(&T); } f2 = "marker"; if (tmpstr) strcpy(tmpstr+1,f2); // Flawfinder: ignore else strcpy(fn,f2); // Flawfinder: ignore fid = fopen(fn,"r"); if (fid != NULL) { size_t n,N; N=0; n=0; while (!feof(fid)) { hdr->AS.auxBUF = (uint8_t*) realloc(hdr->AS.auxBUF,N+bufsiz+1); N += fread(hdr->AS.auxBUF+N, 1, bufsiz, fid); } fclose(fid); hdr->AS.auxBUF[N] = 0; // terminating 0 character N = 0; t = (char*)hdr->AS.auxBUF+strcspn((char*)hdr->AS.auxBUF,"\xA\xD"); t = t+strspn(t,"\xA\xD"); // skip lines 1 and 2 while (t[0]) { char* t1 = t; size_t l1 = strcspn(t1,"="); size_t p2 = strspn(t1+l1,"= ")+l1; char* t2 = t+p2; size_t l2 = strcspn(t2," ,"); size_t p3 = strspn(t2+l2," ,")+l2; char* t3 = t2+p3; size_t l3 = strcspn(t3," ,"); size_t p4 = strspn(t3+l3," ,")+l3; char* t4 = t3+p4; size_t l4 = strcspn(t4,"\xA\xD"); size_t p5 = strspn(t4+l4,"\xA\xD")+l4; t1[l1] = 0; while (isspace(t1[--l1])) t1[l1]=0; t2[l2] = 0; t3[l3] = 0; t4[l4] = 0; t = t4 + p5; if (n+1 >= N) { const size_t sz = 100; hdr->EVENT.TYP = (typeof(hdr->EVENT.TYP)) realloc(hdr->EVENT.TYP,(N+sz)*sizeof(*hdr->EVENT.TYP)); hdr->EVENT.POS = (typeof(hdr->EVENT.POS)) realloc(hdr->EVENT.POS,(N+sz)*sizeof(*hdr->EVENT.POS)); N += sz; } hdr->EVENT.POS[n] = atol(t3); if (!strcmp(t1,"REC")) hdr->EVENT.TYP[n] = 0x7ffe; else if (!strcmp(t1,"MON")) hdr->EVENT.TYP[n] = 0; else if (!strcmp(t1,"TXT")) FreeTextEvent(hdr, n, t4); else FreeTextEvent(hdr, n, t1); if (!strcmp(t2,"off")) hdr->EVENT.TYP[n] |= 0x8000; //fprintf(stdout,"#%u, 0x%04x,%u | t1=<%s> = t2=<%s>, t3=<%s>, t4=<%s>\n",n,hdr->EVENT.TYP[n],hdr->EVENT.POS[n],t1,t2,t3,t4); n++; // t = strtok(NULL,"\xA\xD"); //fprintf(stdout," <%s>\n",t1); } hdr->EVENT.N = n; hdr->EVENT.SampleRate = hdr->SampleRate; // convert2to4_eventtable(hdr); } tmpstr = strrchr(fn,FILESEP); tmpstr[0] = 0; tmpstr = strrchr(fn,FILESEP); f2 = "s_info"; if (tmpstr) strcpy(tmpstr+1,f2); // Flawfinder: ignore else strcpy(fn,f2); // Flawfinder: ignore fid = fopen(fn,"r"); if (fid!=NULL) { count = fread(buf,1,bufsiz-1,fid); fclose(fid); buf[count]=0; // terminating 0 character char *Lastname = NULL; char *Firstname = NULL; struct tm T; t = strtok(buf,"\xA\xD"); t = strtok(NULL,"\xA\xD"); // skip line 1 while (t!=NULL) { char *t1 = strchr(t,'='); t1[0] = 0; while (isspace((++t1)[0])); for (k=strlen(t); (k>0) && isspace(t[--k]); t[k]=0); for (k=strlen(t1); (k>0) && isspace(t1[--k]); t1[k]=0); if (VERBOSE_LEVEL>7) fprintf(stdout,"s_info: <%s> = <%s>\n",t,t1); if (0) {} else if (!strcmp(t,"SubjId")) strncpy(hdr->Patient.Id,t1,MAX_LENGTH_PID); else if (!strcmp(t,"Gender")) switch (t1[0]) { case 'm': case 'M': hdr->Patient.Sex = 1; break; case 'w': case 'W': case 'f': case 'F': hdr->Patient.Sex = 2; break; default: hdr->Patient.Sex = 0; break; } else if (!strcmp(t,"Handedness")) switch (t1[0]) { case 'r': case 'R': hdr->Patient.Handedness = 1; break; case 'l': case 'L': hdr->Patient.Handedness = 2; break; default: hdr->Patient.Handedness = 0; break; } else if (!strcmp(t,"Size")) hdr->Patient.Height = atof(t1); else if (!strcmp(t,"Weight")) hdr->Patient.Weight = atof(t1); else if (!strcmp(t,"FirstName")) { Firstname = t1; } else if (!strcmp(t,"LastName")) { Lastname = t1; } else if (!strcmp(t,"BirthDay")) { int c = sscanf(t1,"%02d.%02d.%04d",&T.tm_mday,&T.tm_mon,&T.tm_year); T.tm_year -=1900; T.tm_mon -=1; T.tm_hour =12; T.tm_min =0; T.tm_sec =0; if (c > 2) hdr->Patient.Birthday = tm_time2gdf_time(&T); } t = strtok(NULL,"\xA\xD"); } size_t l0 = strlen(Firstname); size_t l1 = strlen(Lastname); if (l0+l1+1 <= MAX_LENGTH_NAME) { strcpy(hdr->Patient.Name, Firstname); // Flawfinder: ignore hdr->Patient.Name[l0] = ' '; strcpy(hdr->Patient.Name + l0 + 1, Lastname); // Flawfinder: ignore } else strncpy(hdr->Patient.Name, Lastname, MAX_LENGTH_NAME+1); // Flawfinder: ignore } strcpy(fn,hdr->FileName); // Flawfinder: ignore tmpstr = strrchr(fn,FILESEP); f2 = "rawdata"; if (tmpstr) strcpy(tmpstr+1,f2); // Flawfinder: ignore else strcpy(fn,f2); // Flawfinder: ignore if (VERBOSE_LEVEL>7) fprintf(stdout,"rawdata11: %s \n",f2); hdr->FileName = fn; ifopen(hdr,"r"); if (VERBOSE_LEVEL>7) fprintf(stdout,"rawdata22: %s open=%i\n",f2,hdr->FILE.OPEN); if (hdr->FILE.OPEN) { int16_t a[3]; ifread(a, 2, 3, hdr); hdr->VERSION = a[0]; hdr->HeadLen = 6; switch (a[2]) { case 12: gdftyp = 255+12; break; case 16: gdftyp = 3; break; case 32: gdftyp = 5; break; } for (k=a[1]; k<hdr->NS; k++) hdr->CHANNEL[k].OnOff = 0; for (k=0; k<hdr->NS; k++) { hdr->CHANNEL[k].GDFTYP = gdftyp; } hdr->AS.bpb = (GDFTYP_BITS[gdftyp]*a[1])>>3; hdr->FILE.POS = 0; size_t len = (GDFTYP_BITS[gdftyp]*a[1]*hdr->NRec*hdr->SPR)>>3; if ((GDFTYP_BITS[gdftyp]*a[1]) & 0x07) { /* hack: if SPR*NS*bits are not a multiple of bytes, hdr->AS.bpb would be non-integer causing some problems in SREAD reading the correct number of bytes. This hack makes sure that all data is loaded. */ len++; } hdr->AS.rawdata = (uint8_t*) realloc(hdr->AS.rawdata, len); size_t count = ifread(hdr->AS.rawdata,1,len,hdr); hdr->AS.first = 0; hdr->AS.length = (count<<3)/(GDFTYP_BITS[gdftyp]*a[1]); }
stfio::filetype stfio_file_type(HDRTYPE* hdr) { #ifdef __LIBBIOSIG2_H__ switch (biosig_get_filetype(hdr)) { #else switch (hdr->TYPE) { #endif #if (BIOSIG_VERSION > 10500) case ABF2: return stfio::abf; #endif case ABF: return stfio::abf; case ATF: return stfio::atf; case CFS: return stfio::cfs; case HEKA: return stfio::heka; case HDF: return stfio::hdf5; #if (BIOSIG_VERSION > 10403) case AXG: return stfio::axg; case IBW: return stfio::igor; case SMR: return stfio::son; #endif default: return stfio::none; } } #if (defined(WITH_BIOSIG) || defined(WITH_BIOSIG2)) bool stfio::check_biosig_version(int a, int b, int c) { return (BIOSIG_VERSION >= 10000*a + 100*b + c); } #endif stfio::filetype stfio::importBiosigFile(const std::string &fName, Recording &ReturnData, ProgressInfo& progDlg) { std::string errorMsg("Exception while calling std::importBSFile():\n"); std::string yunits; stfio::filetype type; // ===================================================================================================================== // // importBiosig opens file with libbiosig // - performs an automated identification of the file format // - and decides whether the data is imported through importBiosig (currently CFS, HEKA, ABF1, GDF, and others) // - or handed back to other import*File functions (currently ABF2, AXG, HDF5) // // There are two implementations, level-1 and level-2 interface of libbiosig. // level 1 is used when -DWITH_BIOSIG, -lbiosig // level 2 is used when -DWITH_BIOSIG2, -lbiosig2 // // level 1 is better tested, but it does not provide ABI compatibility between MinGW and VisualStudio // level 2 interface has been developed to provide ABI compatibility, but it is less tested // and the API might still undergo major changes. // ===================================================================================================================== #ifdef __LIBBIOSIG2_H__ HDRTYPE* hdr = sopen( fName.c_str(), "r", NULL ); if (hdr==NULL) { ReturnData.resize(0); return stfio::none; } type = stfio_file_type(hdr); if (biosig_check_error(hdr)) { ReturnData.resize(0); destructHDR(hdr); return type; } enum FileFormat biosig_filetype=biosig_get_filetype(hdr); if (biosig_filetype==ATF || biosig_filetype==ABF2 || biosig_filetype==HDF ) { // ATF, ABF2 and HDF5 support should be handled by importATF, and importABF, and importHDF5 not importBiosig ReturnData.resize(0); destructHDR(hdr); return type; } // earlier versions of biosig support only the file type identification, but did not properly read the files if ( (BIOSIG_VERSION < 10603) && (biosig_filetype==AXG) ) { ReturnData.resize(0); destructHDR(hdr); return type; } // ensure the event table is in chronological order sort_eventtable(hdr); // allocate local memory for intermediate results; const int strSize=100; char str[strSize]; /* count sections and generate list of indices indicating start and end of sweeps */ double fs = biosig_get_eventtable_samplerate(hdr); size_t numberOfEvents = biosig_get_number_of_events(hdr); size_t nsections = biosig_get_number_of_segments(hdr); size_t *SegIndexList = (size_t*)malloc((nsections+1)*sizeof(size_t)); SegIndexList[0] = 0; SegIndexList[nsections] = biosig_get_number_of_samples(hdr); std::string annotationTableDesc = std::string(); for (size_t k=0, n=0; k < numberOfEvents; k++) { uint32_t pos; uint16_t typ; #if BIOSIG_VERSION < 10605 char *desc; #else const char *desc; #endif /* uint32_t dur; uint16_t chn; gdftype timestamp; */ biosig_get_nth_event(hdr, k, &typ, &pos, NULL, NULL, NULL, &desc); if (typ == 0x7ffe) { SegIndexList[++n] = pos; } else if (typ < 256) { sprintf(str,"%f s:\t%s\n", pos/fs, desc); annotationTableDesc += std::string( str ); } } int numberOfChannels = biosig_get_number_of_channels(hdr); /************************************************************************* rescale data to mV and pA *************************************************************************/ for (int ch=0; ch < numberOfChannels; ++ch) { CHANNEL_TYPE *hc = biosig_get_channel(hdr, ch); switch (biosig_channel_get_physdimcode(hc) & 0xffe0) { case 4256: // Volt //biosig_channel_scale_to_unit(hc, "mV"); biosig_channel_change_scale_to_physdimcode(hc, 4274); break; case 4160: // Ampere //biosig_channel_scale_to_unit(hc, "pA"); biosig_channel_change_scale_to_physdimcode(hc, 4181); break; } } /************************************************************************* read bulk data *************************************************************************/ biosig_data_type *data = biosig_get_data(hdr, 0); size_t SPR = biosig_get_number_of_samples(hdr); #ifdef _STFDEBUG std::cout << "Number of events: " << numberOfEvents << std::endl; /*int res = */ hdr2ascii(hdr, stdout, 4); #endif for (int NS=0; NS < numberOfChannels; ) { CHANNEL_TYPE *hc = biosig_get_channel(hdr, NS); Channel TempChannel(nsections); TempChannel.SetChannelName(biosig_channel_get_label(hc)); TempChannel.SetYUnits(biosig_channel_get_physdim(hc)); for (size_t ns=1; ns<=nsections; ns++) { size_t SPS = SegIndexList[ns]-SegIndexList[ns-1]; // length of segment, samples per segment int progbar = int(100.0*(1.0*ns/nsections + NS)/numberOfChannels); std::ostringstream progStr; progStr << "Reading channel #" << NS + 1 << " of " << numberOfChannels << ", Section #" << ns << " of " << nsections; progDlg.Update(progbar, progStr.str()); /* unused // char sweepname[20]; sprintf(sweepname,"sweep %i",(int)ns); */ Section TempSection( SPS, // TODO: hdr->nsamplingpoints[nc][ns] "" // TODO: hdr->sectionname[nc][ns] ); std::copy(&(data[NS*SPR + SegIndexList[ns-1]]), &(data[NS*SPR + SegIndexList[ns]]), TempSection.get_w().begin() ); try { TempChannel.InsertSection(TempSection, ns-1); } catch (...) { ReturnData.resize(0); destructHDR(hdr); return type; } } try { if ((int)ReturnData.size() < numberOfChannels) { ReturnData.resize(numberOfChannels); } ReturnData.InsertChannel(TempChannel, NS++); } catch (...) { ReturnData.resize(0); destructHDR(hdr); return type; } } free(SegIndexList); ReturnData.SetComment ( biosig_get_recording_id(hdr) ); sprintf(str,"v%i.%i.%i (compiled on %s %s)",BIOSIG_VERSION_MAJOR,BIOSIG_VERSION_MINOR,BIOSIG_PATCHLEVEL,__DATE__,__TIME__); std::string Desc = std::string("importBiosig with libbiosig ")+std::string(str) + " "; const char* tmpstr; if ((tmpstr=biosig_get_technician(hdr))) Desc += std::string ("\nTechnician:\t") + std::string (tmpstr) + " "; Desc += std::string( "\nCreated with: "); if ((tmpstr=biosig_get_manufacturer_name(hdr))) Desc += std::string( tmpstr ) + " "; if ((tmpstr=biosig_get_manufacturer_model(hdr))) Desc += std::string( tmpstr ) + " "; if ((tmpstr=biosig_get_manufacturer_version(hdr))) Desc += std::string( tmpstr ) + " "; if ((tmpstr=biosig_get_manufacturer_serial_number(hdr))) Desc += std::string( tmpstr ) + " "; Desc += std::string ("\nUser specified Annotations:\n")+annotationTableDesc; ReturnData.SetFileDescription(Desc); #if (BIOSIG_VERSION > 10509) tmpstr = biosig_get_application_specific_information(hdr); if (tmpstr != NULL) /* MSVC2008 can not properly handle std::string( (char*)NULL ) */ ReturnData.SetGlobalSectionDescription(tmpstr); #endif ReturnData.SetXScale(1000.0/biosig_get_samplerate(hdr)); ReturnData.SetXUnits("ms"); ReturnData.SetScaling("biosig scaling factor"); /************************************************************************* Date and time conversion *************************************************************************/ struct tm T; biosig_get_startdatetime(hdr, &T); ReturnData.SetDateTime(T); destructHDR(hdr); #else // #ifndef __LIBBIOSIG2_H__ HDRTYPE* hdr = sopen( fName.c_str(), "r", NULL ); if (hdr==NULL) { ReturnData.resize(0); return stfio::none; } type = stfio_file_type(hdr); #if !defined(BIOSIG_VERSION) || (BIOSIG_VERSION < 10501) if (hdr->TYPE==ABF) { /* biosig v1.5.0 and earlier does not always return with a proper error message for ABF files. This causes problems with the ABF fallback mechanism */ #else if ( hdr->TYPE==ABF2 ) { // ABF2 support should be handled by importABF not importBiosig ReturnData.resize(0); destructHDR(hdr); return type; } if (hdr->TYPE==ABF && hdr->AS.B4C_ERRNUM) { /* this triggers the fall back mechanims w/o reporting an error message */ #endif ReturnData.resize(0); destructHDR(hdr); // free allocated memory return type; } #if defined(BIOSIG_VERSION) && (BIOSIG_VERSION > 10400) if (hdr->AS.B4C_ERRNUM) { #else if (B4C_ERRNUM) { #endif ReturnData.resize(0); destructHDR(hdr); // free allocated memory return type; } if ( hdr->TYPE==ATF || hdr->TYPE==HDF) { // ATF, HDF5 support should be handled by importATF and importHDF5 not importBiosig ReturnData.resize(0); destructHDR(hdr); return type; } // earlier versions of biosig support only the file type identification, but did not read AXG files #if defined(BIOSIG_VERSION) && (BIOSIG_VERSION > 10403) if ( (BIOSIG_VERSION < 10600) && (hdr->TYPE==AXG) ) { // biosig's AXG import crashes on Windows at this time ReturnData.resize(0); destructHDR(hdr); return type; } #endif // ensure the event table is in chronological order sort_eventtable(hdr); // allocate local memory for intermediate results; const int strSize=100; char str[strSize]; /* count sections and generate list of indices indicating start and end of sweeps */ size_t numberOfEvents = hdr->EVENT.N; size_t LenIndexList = 256; if (LenIndexList > numberOfEvents) LenIndexList = numberOfEvents + 2; size_t *SegIndexList = (size_t*)malloc(LenIndexList*sizeof(size_t)); uint32_t nsections = 0; SegIndexList[nsections] = 0; size_t MaxSectionLength = 0; for (size_t k=0; k <= numberOfEvents; k++) { if (LenIndexList <= nsections+2) { // allocate more memory as needed LenIndexList *=2; SegIndexList = (size_t*)realloc(SegIndexList, LenIndexList*sizeof(size_t)); } /* count number of sections and stores it in nsections; EVENT.TYP==0x7ffe indicate number of breaks between sweeps SegIndexList includes index to first sample and index to last sample, thus, the effective length of SegIndexList is the number of 0x7ffe plus two. */ if (0) ; else if (k >= hdr->EVENT.N) SegIndexList[++nsections] = hdr->NRec*hdr->SPR; else if (hdr->EVENT.TYP[k]==0x7ffe) SegIndexList[++nsections] = hdr->EVENT.POS[k]; else continue; size_t SPS = SegIndexList[nsections]-SegIndexList[nsections-1]; // length of segment, samples per segment if (MaxSectionLength < SPS) MaxSectionLength = SPS; } int numberOfChannels = 0; for (int k=0; k < hdr->NS; k++) if (hdr->CHANNEL[k].OnOff==1) numberOfChannels++; /************************************************************************* rescale data to mV and pA *************************************************************************/ for (int ch=0; ch < hdr->NS; ++ch) { CHANNEL_TYPE *hc = hdr->CHANNEL+ch; if (hc->OnOff != 1) continue; double scale = PhysDimScale(hc->PhysDimCode); switch (hc->PhysDimCode & 0xffe0) { case 4256: // Volt hc->PhysDimCode = 4274; // = PhysDimCode("mV"); scale *=1e3; // V->mV hc->PhysMax *= scale; hc->PhysMin *= scale; hc->Cal *= scale; hc->Off *= scale; break; case 4160: // Ampere hc->PhysDimCode = 4181; // = PhysDimCode("pA"); scale *=1e12; // A->pA hc->PhysMax *= scale; hc->PhysMin *= scale; hc->Cal *= scale; hc->Off *= scale; break; } } /************************************************************************* read bulk data *************************************************************************/ hdr->FLAG.ROW_BASED_CHANNELS = 0; /* size_t blks = */ sread(NULL, 0, hdr->NRec, hdr); biosig_data_type *data = hdr->data.block; size_t SPR = hdr->NRec*hdr->SPR; #ifdef _STFDEBUG std::cout << "Number of events: " << numberOfEvents << std::endl; /*int res = */ hdr2ascii(hdr, stdout, 4); #endif int NS = 0; // number of non-empty channels for (size_t nc=0; nc < hdr->NS; ++nc) { if (hdr->CHANNEL[nc].OnOff == 0) continue; Channel TempChannel(nsections); TempChannel.SetChannelName(hdr->CHANNEL[nc].Label); #if defined(BIOSIG_VERSION) && (BIOSIG_VERSION > 10301) TempChannel.SetYUnits(PhysDim3(hdr->CHANNEL[nc].PhysDimCode)); #else PhysDim(hdr->CHANNEL[nc].PhysDimCode,str); TempChannel.SetYUnits(str); #endif for (size_t ns=1; ns<=nsections; ns++) { size_t SPS = SegIndexList[ns]-SegIndexList[ns-1]; // length of segment, samples per segment int progbar = 100.0*(1.0*ns/nsections + NS)/numberOfChannels; std::ostringstream progStr; progStr << "Reading channel #" << NS + 1 << " of " << numberOfChannels << ", Section #" << ns << " of " << nsections; progDlg.Update(progbar, progStr.str()); /* unused // char sweepname[20]; sprintf(sweepname,"sweep %i",(int)ns); */ Section TempSection( SPS, // TODO: hdr->nsamplingpoints[nc][ns] "" // TODO: hdr->sectionname[nc][ns] ); std::copy(&(data[NS*SPR + SegIndexList[ns-1]]), &(data[NS*SPR + SegIndexList[ns]]), TempSection.get_w().begin() ); try { TempChannel.InsertSection(TempSection, ns-1); } catch (...) { ReturnData.resize(0); destructHDR(hdr); return type; } } try { if ((int)ReturnData.size() < numberOfChannels) { ReturnData.resize(numberOfChannels); } ReturnData.InsertChannel(TempChannel, NS++); } catch (...) { ReturnData.resize(0); destructHDR(hdr); return type; } } free(SegIndexList); ReturnData.SetComment ( hdr->ID.Recording ); sprintf(str,"v%i.%i.%i (compiled on %s %s)",BIOSIG_VERSION_MAJOR,BIOSIG_VERSION_MINOR,BIOSIG_PATCHLEVEL,__DATE__,__TIME__); std::string Desc = std::string("importBiosig with libbiosig ")+std::string(str); if (hdr->ID.Technician) Desc += std::string ("\nTechnician:\t") + std::string (hdr->ID.Technician); Desc += std::string( "\nCreated with: "); if (hdr->ID.Manufacturer.Name) Desc += std::string( hdr->ID.Manufacturer.Name ); if (hdr->ID.Manufacturer.Model) Desc += std::string( hdr->ID.Manufacturer.Model ); if (hdr->ID.Manufacturer.Version) Desc += std::string( hdr->ID.Manufacturer.Version ); if (hdr->ID.Manufacturer.SerialNumber) Desc += std::string( hdr->ID.Manufacturer.SerialNumber ); Desc += std::string ("\nUser specified Annotations:\n"); for (size_t k=0; k < numberOfEvents; k++) { if (hdr->EVENT.TYP[k] < 256) { sprintf(str,"%f s\t",hdr->EVENT.POS[k]/hdr->EVENT.SampleRate); Desc += std::string( str ); if (hdr->EVENT.CodeDesc != NULL) Desc += std::string( hdr->EVENT.CodeDesc[hdr->EVENT.TYP[k]] ); Desc += "\n"; } } ReturnData.SetFileDescription(Desc); // hdr->AS.bci2000 is an alias to hdr->AS.fpulse, which available only in libbiosig v1.6.0 or later if (hdr->AS.bci2000) ReturnData.SetGlobalSectionDescription(std::string(hdr->AS.bci2000)); ReturnData.SetXScale(1000.0/hdr->SampleRate); ReturnData.SetXUnits("ms"); ReturnData.SetScaling("biosig scaling factor"); /************************************************************************* Date and time conversion *************************************************************************/ struct tm T; #if (BIOSIG_VERSION_MAJOR > 0) gdf_time2tm_time_r(hdr->T0, &T); #else struct tm* Tp; Tp = gdf_time2tm_time(hdr->T0); T = *Tp; #endif ReturnData.SetDateTime(T); destructHDR(hdr); #endif return stfio::biosig; } // ===================================================================================================================== // // Save file with libbiosig into GDF format // // There basically two implementations, one with libbiosig before v1.6.0 and // and one for libbiosig v1.6.0 and later // // ===================================================================================================================== bool stfio::exportBiosigFile(const std::string& fName, const Recording& Data, stfio::ProgressInfo& progDlg) { /* converts the internal data structure to libbiosig's internal structure and saves the file as gdf file. The data in converted into the raw data format, and not into the common data matrix. */ #ifdef __LIBBIOSIG2_H__ size_t numberOfChannels = Data.size(); HDRTYPE* hdr = constructHDR(numberOfChannels, 0); /* Initialize all header parameters */ biosig_set_filetype(hdr, GDF); biosig_set_startdatetime(hdr, Data.GetDateTime()); const char *xunits = Data.GetXUnits().c_str(); uint16_t pdc = PhysDimCode(xunits); if ((pdc & 0xffe0) != PhysDimCode("s")) { fprintf(stderr,"Stimfit exportBiosigFile: xunits [%s] has not proper units, assume [ms]\n",Data.GetXUnits().c_str()); pdc = PhysDimCode("ms"); } double fs = 1.0/(PhysDimScale(pdc) * Data.GetXScale()); biosig_set_samplerate(hdr, fs); #if (BIOSIG_VERSION < 10700) biosig_set_flags(hdr, 0, 0, 0); #else biosig_reset_flag(hdr, BIOSIG_FLAG_COMPRESSION | BIOSIG_FLAG_UCAL | BIOSIG_FLAG_OVERFLOWDETECTION | BIOSIG_FLAG_ROW_BASED_CHANNELS ); #endif size_t k, m, numberOfEvents=0; size_t NRec=0; // corresponds to hdr->NRec size_t SPR=1; // corresponds to hdr->SPR size_t chSPR=0; // corresponds to hc->SPR /* Initialize all channel parameters */ #ifndef DONOTUSE_DYNAMIC_ALLOCATION_FOR_CHANSPR size_t *chanSPR = (size_t*)malloc(numberOfChannels*sizeof(size_t)); #endif for (k = 0; k < numberOfChannels; ++k) { CHANNEL_TYPE *hc = biosig_get_channel(hdr, k); biosig_channel_set_datatype_to_double(hc); biosig_channel_set_scaling(hc, 1e9, -1e9, 1e9, -1e9); biosig_channel_set_label(hc, Data[k].GetChannelName().c_str()); biosig_channel_set_physdim(hc, Data[k].GetYUnits().c_str()); biosig_channel_set_filter(hc, NAN, NAN, NAN); biosig_channel_set_timing_offset(hc, 0.0); biosig_channel_set_impedance(hc, NAN); chSPR = SPR; // each segment gets one marker, roughly numberOfEvents += Data[k].size(); size_t m,len = 0; for (len=0, m = 0; m < Data[k].size(); ++m) { unsigned div = lround(Data[k][m].GetXScale()/Data.GetXScale()); chSPR = lcm(chSPR,div); // sampling interval of m-th segment in k-th channel len += div*Data[k][m].size(); } SPR = lcm(SPR, chSPR); /* hc->SPR (i.e. chSPR) is 'abused' to store final hdr->SPR/hc->SPR, this is corrected in the loop below its a hack to avoid the need for another malloc(). */ #ifdef DONOTUSE_DYNAMIC_ALLOCATION_FOR_CHANSPR biosig_channel_set_samples_per_record(hc, chSPR); #else chanSPR[k]=chSPR; #endif if (k==0) { NRec = len; } else if ((size_t)NRec != len) { destructHDR(hdr); throw std::runtime_error("File can't be exported:\n" "No data or traces have different sizes" ); return false; } } biosig_set_number_of_samples(hdr, NRec, SPR); size_t bpb = 0; for (k = 0; k < numberOfChannels; ++k) { CHANNEL_TYPE *hc = biosig_get_channel(hdr, k); // the 'abuse' of hc->SPR described above is corrected #ifdef DONOTUSE_DYNAMIC_ALLOCATION_FOR_CHANSPR size_t spr = biosig_channel_get_samples_per_record(hc); spr = SPR / spr; biosig_channel_set_samples_per_record(hc, spr); #else size_t spr = SPR/chanSPR[k]; chanSPR[k] = spr; #endif bpb += spr * 8; /* its always double */ } /*** build Event table for storing segment information pre-allocate memory for even table ***/ numberOfEvents *= 2; // about two events per segment biosig_set_number_of_events(hdr, numberOfEvents); /* check whether all segments have same size */ { char flag = (numberOfChannels > 0); size_t m, POS, pos; for (k=0; k < numberOfChannels; ++k) { pos = Data[k].size(); if (k==0) POS = pos; else flag &= (POS == pos); } for (m=0; flag && (m < Data[(size_t)0].size()); ++m) { for (k=0; k < biosig_get_number_of_channels(hdr); ++k) { pos = Data[k][m].size() * lround(Data[k][m].GetXScale()/Data.GetXScale()); if (k==0) POS = pos; else flag &= (POS == pos); } } if (!flag) { destructHDR(hdr); throw std::runtime_error( "File can't be exported:\n" "Traces have different sizes or no channels found" ); return false; } } size_t N=0; k=0; size_t pos = 0; for (m=0; m < (Data[k].size()); ++m) { if (pos > 0) { uint16_t typ=0x7ffe; uint32_t pos32=pos; uint16_t chn=0; uint32_t dur=0; // set break marker biosig_set_nth_event(hdr, N++, &typ, &pos32, &chn, &dur, NULL, NULL); /* // set annotation const char *Desc = Data[k][m].GetSectionDescription().c_str(); if (Desc != NULL && strlen(Desc)>0) biosig_set_nth_event(hdr, N++, NULL, &pos32, &chn, &dur, NULL, Desc); // TODO */ } pos += Data[k][m].size() * lround(Data[k][m].GetXScale()/Data.GetXScale()); } biosig_set_number_of_events(hdr, N); biosig_set_eventtable_samplerate(hdr, fs); sort_eventtable(hdr); /* convert data into GDF rawdata from */ uint8_t *rawdata = (uint8_t*)malloc(bpb * NRec); size_t bi=0; for (k=0; k < numberOfChannels; ++k) { CHANNEL_TYPE *hc = biosig_get_channel(hdr, k); #ifdef DONOTUSE_DYNAMIC_ALLOCATION_FOR_CHANSPR size_t chSPR = biosig_channel_get_samples_per_record(hc); #else size_t chSPR = chanSPR[k]; #endif size_t m,n,len=0; for (m=0; m < Data[k].size(); ++m) { size_t div = lround(Data[k][m].GetXScale()/Data.GetXScale()); size_t div2 = SPR/div; // TODO: avoid using hdr->SPR // fprintf(stdout,"k,m,div,div2: %i,%i,%i,%i\n",(int)k,(int)m,(int)div,(int)div2); // for (n=0; n < Data[k][m].size(); ++n) { uint64_t val; double d = Data[k][m][n]; #if !defined(__MINGW32__) && !defined(_MSC_VER) && !defined(__APPLE__) val = htole64(*(uint64_t*)&d); #else val = *(uint64_t*)&d; #endif size_t p, spr = (len + n*div) / SPR; for (p=0; p < div2; p++) *(uint64_t*)(rawdata + bi + bpb * spr + p*8) = val; } len += div*Data[k][m].size(); } bi += chSPR*8; } #ifndef DONOTUSE_DYNAMIC_ALLOCATION_FOR_CHANSPR if (chanSPR) free(chanSPR); #endif /****************************** write to file *******************************/ std::string errorMsg("Exception while calling std::exportBiosigFile():\n"); hdr = sopen( fName.c_str(), "w", hdr ); if (serror2(hdr)) { errorMsg += biosig_get_errormsg(hdr); destructHDR(hdr); throw std::runtime_error(errorMsg.c_str()); return false; } ifwrite(rawdata, bpb, NRec, hdr); sclose(hdr); destructHDR(hdr); free(rawdata); #else // #ifndef __LIBBIOSIG2_H__ HDRTYPE* hdr = constructHDR(Data.size(), 0); assert(hdr->NS == Data.size()); /* Initialize all header parameters */ hdr->TYPE = GDF; #if (BIOSIG_VERSION >= 10508) /* transition in biosig to rename HDR->VERSION to HDR->Version to avoid name space conflict with macro VERSION */ hdr->Version = 3.0; // select latest supported version of GDF format #else hdr->VERSION = 3.0; // select latest supported version of GDF format #endif struct tm t = Data.GetDateTime(); hdr->T0 = tm_time2gdf_time(&t); const char *xunits = Data.GetXUnits().c_str(); #if (BIOSIG_VERSION_MAJOR > 0) uint16_t pdc = PhysDimCode(xunits); #else uint16_t pdc = PhysDimCode((char*)xunits); #endif if ((pdc & 0xffe0) == PhysDimCode("s")) { fprintf(stderr,"Stimfit exportBiosigFile: xunits [%s] has not proper units, assume [ms]\n",Data.GetXUnits().c_str()); pdc = PhysDimCode("ms"); } hdr->SampleRate = 1.0/(PhysDimScale(pdc) * Data.GetXScale()); hdr->SPR = 1; hdr->FLAG.UCAL = 0; hdr->FLAG.OVERFLOWDETECTION = 0; hdr->FILE.COMPRESSION = 0; /* Initialize all channel parameters */ size_t k, m; for (k = 0; k < hdr->NS; ++k) { CHANNEL_TYPE *hc = hdr->CHANNEL+k; hc->PhysMin = -1e9; hc->PhysMax = 1e9; hc->DigMin = -1e9; hc->DigMax = 1e9; hc->Cal = 1.0; hc->Off = 0.0; /* Channel descriptions. */ strncpy(hc->Label, Data[k].GetChannelName().c_str(), MAX_LENGTH_LABEL); #if (BIOSIG_VERSION_MAJOR > 0) hc->PhysDimCode = PhysDimCode(Data[k].GetYUnits().c_str()); #else hc->PhysDimCode = PhysDimCode((char*)Data[k].GetYUnits().c_str()); #endif hc->OnOff = 1; hc->LeadIdCode = 0; hc->TOffset = 0.0; hc->Notch = NAN; hc->LowPass = NAN; hc->HighPass = NAN; hc->Impedance= NAN; hc->SPR = hdr->SPR; hc->GDFTYP = 17; // double // each segment gets one marker, roughly hdr->EVENT.N += Data[k].size(); size_t m,len = 0; for (len=0, m = 0; m < Data[k].size(); ++m) { unsigned div = lround(Data[k][m].GetXScale()/Data.GetXScale()); hc->SPR = lcm(hc->SPR,div); // sampling interval of m-th segment in k-th channel len += div*Data[k][m].size(); } hdr->SPR = lcm(hdr->SPR, hc->SPR); if (k==0) { hdr->NRec = len; } else if ((size_t)hdr->NRec != len) { destructHDR(hdr); throw std::runtime_error("File can't be exported:\n" "No data or traces have different sizes" ); return false; } } hdr->AS.bpb = 0; for (k = 0; k < hdr->NS; ++k) { CHANNEL_TYPE *hc = hdr->CHANNEL+k; hc->SPR = hdr->SPR / hc->SPR; hc->bi = hdr->AS.bpb; hdr->AS.bpb += hc->SPR * 8; /* its always double */ } /*** build Event table for storing segment information ***/ size_t N = hdr->EVENT.N * 2; // about two events per segment hdr->EVENT.POS = (uint32_t*)realloc(hdr->EVENT.POS, N * sizeof(*hdr->EVENT.POS)); hdr->EVENT.DUR = (uint32_t*)realloc(hdr->EVENT.DUR, N * sizeof(*hdr->EVENT.DUR)); hdr->EVENT.TYP = (uint16_t*)realloc(hdr->EVENT.TYP, N * sizeof(*hdr->EVENT.TYP)); hdr->EVENT.CHN = (uint16_t*)realloc(hdr->EVENT.CHN, N * sizeof(*hdr->EVENT.CHN)); #if (BIOSIG_VERSION >= 10500) hdr->EVENT.TimeStamp = (gdf_time*)realloc(hdr->EVENT.TimeStamp, N * sizeof(gdf_time)); #endif /* check whether all segments have same size */ { char flag = (hdr->NS>0); size_t m, POS, pos; for (k=0; k < hdr->NS; ++k) { pos = Data[k].size(); if (k==0) POS = pos; else flag &= (POS == pos); } for (m=0; flag && (m < Data[(size_t)0].size()); ++m) { for (k=0; k < hdr->NS; ++k) { pos = Data[k][m].size() * lround(Data[k][m].GetXScale()/Data.GetXScale()); if (k==0) POS = pos; else flag &= (POS == pos); } } if (!flag) { destructHDR(hdr); throw std::runtime_error( "File can't be exported:\n" "Traces have different sizes or no channels found" ); return false; } } N=0; k=0; size_t pos = 0; for (m=0; m < (Data[k].size()); ++m) { if (pos > 0) { // start of new segment after break hdr->EVENT.POS[N] = pos; hdr->EVENT.TYP[N] = 0x7ffe; hdr->EVENT.CHN[N] = 0; hdr->EVENT.DUR[N] = 0; N++; } #if 0 // event description hdr->EVENT.POS[N] = pos; FreeTextEvent(hdr, N, "myevent"); //FreeTextEvent(hdr, N, Data[k][m].GetSectionDescription().c_str()); // TODO hdr->EVENT.CHN[N] = k; hdr->EVENT.DUR[N] = 0; N++; #endif pos += Data[k][m].size() * lround(Data[k][m].GetXScale()/Data.GetXScale()); } hdr->EVENT.N = N; hdr->EVENT.SampleRate = hdr->SampleRate; sort_eventtable(hdr); /* convert data into GDF rawdata from */ hdr->AS.rawdata = (uint8_t*)realloc(hdr->AS.rawdata, hdr->AS.bpb*hdr->NRec); for (k=0; k < hdr->NS; ++k) { CHANNEL_TYPE *hc = hdr->CHANNEL+k; size_t m,n,len=0; for (m=0; m < Data[k].size(); ++m) { size_t div = lround(Data[k][m].GetXScale()/Data.GetXScale()); size_t div2 = hdr->SPR/div; // fprintf(stdout,"k,m,div,div2: %i,%i,%i,%i\n",(int)k,(int)m,(int)div,(int)div2); // for (n=0; n < Data[k][m].size(); ++n) { uint64_t val; double d = Data[k][m][n]; #if !defined(__MINGW32__) && !defined(_MSC_VER) && !defined(__APPLE__) val = htole64(*(uint64_t*)&d); #else val = *(uint64_t*)&d; #endif size_t p, spr = (len + n*div) / hdr->SPR; for (p=0; p < div2; p++) *(uint64_t*)(hdr->AS.rawdata + hc->bi + hdr->AS.bpb * spr + p*8) = val; } len += div*Data[k][m].size(); } } /****************************** write to file *******************************/ std::string errorMsg("Exception while calling std::exportBiosigFile():\n"); hdr = sopen( fName.c_str(), "w", hdr ); #if (BIOSIG_VERSION > 10400) if (serror2(hdr)) { errorMsg += hdr->AS.B4C_ERRMSG; #else if (serror()) { errorMsg += B4C_ERRMSG; #endif destructHDR(hdr); throw std::runtime_error(errorMsg.c_str()); return false; } ifwrite(hdr->AS.rawdata, hdr->AS.bpb, hdr->NRec, hdr); sclose(hdr); destructHDR(hdr); #endif return true; }
/**************************************************************************** sopen_heka reads heka format if itx is not null, the file is converted into an ITX formated file and streamed to itx, too. ****************************************************************************/ void sopen_heka(HDRTYPE* hdr, FILE *itx) { size_t count = hdr->HeadLen; if (hdr->TYPE==HEKA && hdr->VERSION > 1) { int32_t Levels=0; uint16_t k; //int32_t *Sizes=NULL; int32_t Counts[5], counts[5]; //, Sizes[5]; memset(Counts,0,20); memset(counts,0,20); //memset(Sizes,0,20); uint32_t StartOfData=0,StartOfPulse=0; union { struct { int32_t Root; int32_t Group; int32_t Series; int32_t Sweep; int32_t Trace; } Rec; int32_t all[5]; } Sizes; // HEKA PatchMaster file format count = hdr->HeadLen; struct stat FileBuf; stat(hdr->FileName,&FileBuf); hdr->AS.Header = (uint8_t*)realloc(hdr->AS.Header, FileBuf.st_size); count += ifread(hdr->AS.Header+count, 1, 1024-count, hdr); hdr->HeadLen = count; hdr->FILE.LittleEndian = *(uint8_t*)(hdr->AS.Header+52) > 0; char SWAP = ( hdr->FILE.LittleEndian && (__BYTE_ORDER == __BIG_ENDIAN)) \ || (!hdr->FILE.LittleEndian && (__BYTE_ORDER == __LITTLE_ENDIAN)); if (SWAP) { biosigERROR(hdr, B4C_FORMAT_UNSUPPORTED, "Heka/Patchmaster format requires data swapping - this is not supported yet."); return; } SWAP = 0; // might be useful for compile time optimization /* get file size and read whole file */ count += ifread(hdr->AS.Header+count, 1, FileBuf.st_size - count, hdr); if (VERBOSE_LEVEL>7) fprintf(stdout,"%s (line %i) %s(...): %i bytes read\n",__FILE__,__LINE__,__func__, count); // double oTime; uint32_t nItems; if (hdr->FILE.LittleEndian) { // oTime = lef64p(hdr->AS.Header+40); // not used nItems = leu32p(hdr->AS.Header+48); } else { // oTime = bef64p(hdr->AS.Header+40); // not used nItems = beu32p(hdr->AS.Header+48); } if (VERBOSE_LEVEL>7) fprintf(stdout,"%s (line %i) %s(...): nItems=%i\n",__FILE__,__LINE__,__func__, nItems); if (hdr->VERSION == 1) { Sizes.Rec.Root = 544; Sizes.Rec.Group = 128; Sizes.Rec.Series = 1120; Sizes.Rec.Sweep = 160; Sizes.Rec.Trace = 296; } else if (hdr->VERSION == 2) for (k=0; k < min(12,nItems); k++) { if (VERBOSE_LEVEL>7) fprintf(stdout,"%s (line %i): HEKA nItems=%i\n",__func__,__LINE__, k); uint32_t start = *(uint32_t*)(hdr->AS.Header+k*16+64); uint32_t length = *(uint32_t*)(hdr->AS.Header+k*16+64+4); if (SWAP) { start = bswap_32(start); length = bswap_32(length); } uint8_t *ext = hdr->AS.Header + k*16 + 64 + 8; if (VERBOSE_LEVEL>7) fprintf(stdout,"%s (line %i): HEKA #%i: <%s> [%i:+%i]\n",__func__,__LINE__,k,ext,start,length); if (!start) break; if ((start+8) > count) { biosigERROR(hdr, B4C_INCOMPLETE_FILE, "Heka/Patchmaster: file is corrupted - segment with pulse data is not available!"); return; } if (!memcmp(ext,".pul\0\0\0\0",8)) { // find pulse data ifseek(hdr, start, SEEK_SET); //magic = *(int32_t*)(hdr->AS.Header+start); Levels = *(int32_t*)(hdr->AS.Header+start+4); if (SWAP) Levels = bswap_32(Levels); if (Levels>5) { biosigERROR(hdr, B4C_FORMAT_UNSUPPORTED, "Heka/Patchmaster format with more than 5 levels not supported"); return; } if (VERBOSE_LEVEL>7) fprintf(stdout,"%s (line %i): HEKA #%i Levels=%i\n",__func__,__LINE__,k,Levels); memcpy(Sizes.all,hdr->AS.Header+start+8,sizeof(int32_t)*Levels); if (SWAP) { int l; for (l=0; l < Levels; l++) Sizes.all[l] = bswap_32(Sizes.all[l]); } if (VERBOSE_LEVEL>7) {int l; for (l=0; l < Levels; l++) fprintf(stdout,"%s (line %i): HEKA #%i %i\n",__func__,__LINE__,l, Sizes.all[l]); } StartOfPulse = start + 8 + 4 * Levels; } else if (!memcmp(ext,".dat\0\0\0\0",8)) { StartOfData = start; } } if (VERBOSE_LEVEL>7) fprintf(stdout,"%s (line %i) %s(...): \n",__FILE__,__LINE__,__func__); // if (!Sizes) free(Sizes); Sizes=NULL; /* DONE: HEKA, check channel number and label pass 1: + get number of sweeps + get number of channels + check whether all traces of a single sweep have the same SPR, and Fs + check whether channelnumber (TrAdcChannel), scaling (DataScaler) and Label fit among all sweeps + extract the total number of samples + physical units + level 4 may have no children + count event descriptions Level2/SeLabel pass 2: + initialize data to NAN + skip sweeps if selected channel is not in it + Y scale, physical scale + Event.CodeDescription, Events, resampling */ uint32_t k1=0, k2=0, k3=0, k4=0; uint32_t K1=0, K2=0, K3=0, K4=0, K5=0; double t; size_t pos; // read K1 if (SWAP) { K1 = bswap_32(*(uint32_t*)(hdr->AS.Header + StartOfPulse + Sizes.Rec.Root)); hdr->VERSION = bswap_32(*(uint32_t*)(hdr->AS.Header + StartOfPulse)); union { double f64; uint64_t u64; } c; c.u64 = bswap_64(*(uint64_t*)(hdr->AS.Header + StartOfPulse + 520)); t = c.f64; } else { K1 = (*(uint32_t*)(hdr->AS.Header + StartOfPulse + Sizes.Rec.Root)); hdr->VERSION = (*(uint32_t*)(hdr->AS.Header + StartOfPulse)); t = (*(double*)(hdr->AS.Header + StartOfPulse + 520)); } if (VERBOSE_LEVEL>7) fprintf(stdout,"%s (line %i) %s(...): \n",__FILE__,__LINE__,__func__); hdr->T0 = heka2gdftime(t); // this is when when heka was started, data is recorded later. hdr->SampleRate = 0.0; double *DT = NULL; // list of sampling intervals per channel hdr->SPR = 0; if (VERBOSE_LEVEL>7) fprintf(stdout,"%s (line %i) %s(...): %p\n",__FILE__,__LINE__,__func__,hdr->EVENT.CodeDesc); /******************************************************************************************************* HEKA: read structural information *******************************************************************************************************/ pos = StartOfPulse + Sizes.Rec.Root + 4; size_t EventN=0; for (k1=0; k1<K1; k1++) { // read group if (VERBOSE_LEVEL>7) fprintf(stdout,"HEKA L1 @%i=\t%i/%i \n",(int)(pos+StartOfData),k1,K1); pos += Sizes.Rec.Group+4; // read number of children K2 = (*(uint32_t*)(hdr->AS.Header+pos-4)); hdr->AS.auxBUF = (uint8_t*)realloc(hdr->AS.auxBUF,K2*33); // used to store name of series for (k2=0; k2<K2; k2++) { // read series union { double f64; uint64_t u64; } Delay; char *SeLabel = (char*)(hdr->AS.Header+pos+4); // max 32 bytes strncpy((char*)hdr->AS.auxBUF + 33*k2, (char*)hdr->AS.Header+pos+4, 32); hdr->AS.auxBUF[33*k2+32] = 0; SeLabel = (char*)hdr->AS.auxBUF + 33*k2; double tt = *(double*)(hdr->AS.Header+pos+136); // time of series. TODO: this time should be taken into account Delay.u64 = bswap_64(*(uint64_t*)(hdr->AS.Header+pos+472+176)); gdf_time t = heka2gdftime(tt); struct tm tm; gdf_time2tm_time_r(t,&tm); if (VERBOSE_LEVEL>7) fprintf(stdout,"HEKA L2 @%i=%s %f\t%i/%i %i/%i t=%.17g %s\n",(int)(pos+StartOfData),SeLabel,Delay.f64,k1,K1,k2,K2,ldexp(t,-32),asctime(&tm)); pos += Sizes.Rec.Series + 4; // read number of children K3 = (*(uint32_t*)(hdr->AS.Header+pos-4)); if (EventN <= hdr->EVENT.N + K3 + 2) { EventN = max(max(16,EventN),hdr->EVENT.N+K3+2) * 2; if (reallocEventTable(hdr, EventN) == SIZE_MAX) { biosigERROR(hdr, B4C_MEMORY_ALLOCATION_FAILED, "Allocating memory for event table failed."); return; }; } if (!hdr->AS.SegSel[0] && !hdr->AS.SegSel[1] && !hdr->AS.SegSel[2]) { // in case of reading the whole file (no sweep selection), include marker for start of series FreeTextEvent(hdr, hdr->EVENT.N, SeLabel); hdr->EVENT.POS[hdr->EVENT.N] = hdr->SPR; // within reading the structure, hdr->SPR is used as a intermediate variable counting the number of samples #if (BIOSIG_VERSION >= 10500) hdr->EVENT.TimeStamp[hdr->EVENT.N] = t; #endif hdr->EVENT.N++; } for (k3=0; k3<K3; k3++) { // read sweep hdr->NRec++; // increase number of sweeps size_t SPR = 0, spr = 0; gdf_time t = heka2gdftime(*(double*)(hdr->AS.Header+pos+48)); // time of sweep. TODO: this should be taken into account gdf_time2tm_time_r(t,&tm); if (VERBOSE_LEVEL>7) fprintf(stdout,"HEKA L3 @%i= %fHz\t%i/%i %i/%i %i/%i %s\n",(int)(pos+StartOfData),hdr->SampleRate,k1,K1,k2,K2,k3,K3,asctime(&tm)); char flagSweepSelected = (hdr->AS.SegSel[0]==0 || k1+1==hdr->AS.SegSel[0]) && (hdr->AS.SegSel[1]==0 || k2+1==hdr->AS.SegSel[1]) && (hdr->AS.SegSel[2]==0 || k3+1==hdr->AS.SegSel[2]); // hdr->SPR if (hdr->SPR==0) hdr->T0 = t; // start time of first recording determines the start time of the recording else if (flagSweepSelected && hdr->SPR > 0) { // marker for start of sweep hdr->EVENT.POS[hdr->EVENT.N] = hdr->SPR; // within reading the structure, hdr->SPR is used as a intermediate variable counting the number of samples hdr->EVENT.TYP[hdr->EVENT.N] = 0x7ffe; #if (BIOSIG_VERSION >= 10500) hdr->EVENT.TimeStamp[hdr->EVENT.N] = t; #endif hdr->EVENT.N++; } pos += Sizes.Rec.Sweep + 4; // read number of children K4 = (*(uint32_t*)(hdr->AS.Header+pos-4)); for (k4=0; k4<K4; k4++) { // read trace double DigMin, DigMax; uint16_t gdftyp = 0; uint32_t ns = (*(uint32_t*)(hdr->AS.Header+pos+36)); uint32_t DataPos = (*(uint32_t*)(hdr->AS.Header+pos+40)); spr = (*(uint32_t*)(hdr->AS.Header+pos+44)); double DataScaler= (*(double*)(hdr->AS.Header+pos+72)); double Toffset = (*(double*)(hdr->AS.Header+pos+80)); // time offset of uint16_t pdc = PhysDimCode((char*)(hdr->AS.Header + pos + 96)); double dT = (*(double*)(hdr->AS.Header+pos+104)); //double XStart = (*(double*)(hdr->AS.Header+pos+112)); uint16_t XUnits = PhysDimCode((char*)(hdr->AS.Header+pos+120)); double YRange = (*(double*)(hdr->AS.Header+pos+128)); double YOffset = (*(double*)(hdr->AS.Header+pos+136)); double Bandwidth = (*(double*)(hdr->AS.Header+pos+144)); //double PipetteResistance = (*(double*)(hdr->AS.Header+pos+152)); double RsValue = (*(double*)(hdr->AS.Header+pos+192)); uint8_t ValidYRange = hdr->AS.Header[pos+220]; uint16_t AdcChan = (*(uint16_t*)(hdr->AS.Header+pos+222)); /* obsolete: range is defined by DigMin/DigMax * DataScaler + YOffset double PhysMin = (*(double*)(hdr->AS.Header+pos+224)); double PhysMax = (*(double*)(hdr->AS.Header+pos+232)); */ if (VERBOSE_LEVEL>7) fprintf(stdout, "%s (line %i): %i %i %i %i %i %g %g 0x%x xUnits=%i %g %g %g %g %i %i\n", __FILE__,__LINE__, k1, k2, k3, k4, ns, DataScaler, Toffset, pdc, XUnits, YRange, YOffset, Bandwidth, RsValue, ValidYRange, AdcChan); switch (hdr->AS.Header[pos+70]) { case 0: gdftyp = 3; //int16 /* It seems that the range is 1.024*(2^15-1)/2^15 nA or V and symetric around zero. i.e. YOffset is zero */ DigMax = ldexp(1.0,15) - 1.0; DigMin = -DigMax; break; case 1: gdftyp = 5; //int32 DigMax = ldexp(1.0, 31) - 1.0; DigMin = -DigMax; break; case 2: gdftyp = 16; //float32 DigMax = 1e9; DigMin = -1e9; break; case 3: gdftyp = 17; //float64 DigMax = 1e9; DigMin = -1e9; break; default: DigMax = NAN; DigMin = NAN; biosigERROR(hdr, B4C_FORMAT_UNSUPPORTED, "Heka/Patchmaster: data type not supported"); }; if (SWAP) { AdcChan = bswap_16(AdcChan); ns = bswap_32(ns); DataPos = bswap_32(DataPos); spr = bswap_32(spr); // avoid breaking strict-aliasing rules union { double f64; uint64_t u64; } c; c.f64 = dT; c.u64 = bswap_64(c.u64); dT = c.f64; c.f64 = YRange; c.u64 = bswap_64(c.u64); YRange = c.f64; c.f64 = YOffset; c.u64 = bswap_64(c.u64); YOffset = c.f64; //c.f64 = PhysMax; c.u64 = bswap_64(c.u64); PhysMax = c.f64; //c.f64 = PhysMin; c.u64 = bswap_64(c.u64); PhysMin = c.f64; c.f64 = Toffset; c.u64 = bswap_64(c.u64); Toffset = c.f64; } if (YOffset != 0.0) fprintf(stderr,"!!! WARNING !!! HEKA: the offset is not zero - " "this case is not tested and might result in incorrect scaling of " "the data,\n!!! YOU ARE WARNED !!!\n"); // scale to standard units - no prefix double Cal = DataScaler * PhysDimScale(pdc); double Off = YOffset * PhysDimScale(pdc); pdc &= 0xffe0; float Fs = 1.0 / ( dT * PhysDimScale(XUnits) ) ; // float is used to avoid spurios accuracy, round to single precision accuracy if (flagSweepSelected) { if (hdr->SampleRate <= 0.0) hdr->SampleRate = Fs; if (fabs(hdr->SampleRate - Fs) > 1e-9*Fs) { long DIV1 = 1, DIV2 = 1; rational(hdr->SampleRate*dT*PhysDimScale(XUnits), 1e-6, &DIV2, &DIV1); if (DIV1 > 1) { if ( ((size_t)DIV1 * hdr->SPR) > 0xffffffffffffffff) { fprintf(stderr,"!!! WARNING sopen_heka(%s) !!! due to resampling, the data will have more then 2^31 samples !!!\n", hdr->FileName); biosigERROR(hdr,B4C_FORMAT_UNSUPPORTED,"HEKA file has more than 2^32 samples - this is not supported yet"); } hdr->SPR *= DIV1; hdr->SampleRate *= DIV1; hdr->EVENT.SampleRate = hdr->SampleRate; size_t n = 0; while (n < hdr->EVENT.N) hdr->EVENT.POS[n++] *= DIV1; } if (DIV2 > 1) spr *= DIV2; } // samples per sweep if (k4==0) SPR = spr; else if (SPR != spr) { biosigERROR(hdr, B4C_FORMAT_UNSUPPORTED, "Heka/Patchmaster: number of samples among channels within a single sweep do not match."); return; } } char *Label = (char*)hdr->AS.Header+pos+4; for (ns=0; ns < hdr->NS; ns++) { if (!strcmp(hdr->CHANNEL[ns].Label,Label)) break; } if (VERBOSE_LEVEL>7) fprintf(stdout,"HEKA L4 @%i= #%i,%i, %s %f %fHz\t%i/%i %i/%i %i/%i %i/%i \n",(int)(pos+StartOfData),ns,AdcChan,Label,hdr->SampleRate,Fs,k1,K1,k2,K2,k3,K3,k4,K4); CHANNEL_TYPE *hc; if (ns >= hdr->NS) { hdr->NS = ns + 1; #ifdef WITH_TIMESTAMPCHANNEL // allocate memory for an extra time stamp channel, which is define only after the end of the channel loop - see below hdr->CHANNEL = (CHANNEL_TYPE*) realloc(hdr->CHANNEL, (hdr->NS + 1) * sizeof(CHANNEL_TYPE)); #else hdr->CHANNEL = (CHANNEL_TYPE*) realloc(hdr->CHANNEL, hdr->NS * sizeof(CHANNEL_TYPE)); #endif hc = hdr->CHANNEL + ns; strncpy(hc->Label, Label, max(32, MAX_LENGTH_LABEL)); hc->Label[max(32,MAX_LENGTH_LABEL)] = 0; hc->Transducer[0] = 0; hc->SPR = 1; hc->PhysDimCode = pdc; hc->OnOff = 1; hc->GDFTYP = gdftyp; hc->LeadIdCode = 0; hc->DigMin = DigMin; hc->DigMax = DigMax; // TODO: case of non-zero YOffset is not tested // hc->PhysMax = DigMax * Cal + Off; hc->PhysMin = DigMin * Cal + Off; hc->Cal = Cal; hc->Off = Off; hc->TOffset = Toffset; #ifndef NDEBUG double Cal2 = (hc->PhysMax - hc->PhysMin) / (hc->DigMax - hc->DigMin); double Off2 = hc->PhysMin - Cal2 * hc->DigMin; double Off3 = hc->PhysMax - Cal2 * hc->DigMax; if (VERBOSE_LEVEL>6) fprintf(stdout,"HEKA L5 @%i= #%i,%i, %s %g/%g %g/%g \n",(int)(pos+StartOfData),ns,AdcChan,Label,Cal,Cal2,Off,Off2); assert(Cal==Cal2); assert(Off==Off2); assert(Off==Off3); #endif /* TODO: fix remaining channel header */ /* LowPass, HighPass, Notch, Impedance, */ hc->HighPass = NAN; hc->LowPass = (Bandwidth > 0) ? Bandwidth : NAN; hc->Notch = NAN; hc->Impedance = (RsValue > 0) ? RsValue : NAN; DT = (double*) realloc(DT, hdr->NS*sizeof(double)); DT[ns] = dT; } else { /* channel has been already defined in earlier sweep. check compatibility and adapt internal format when needed */ hc = hdr->CHANNEL + ns; double PhysMax = DigMax * Cal + Off; double PhysMin = DigMin * Cal + Off; // get max value to avoid false positive saturation detection when scaling changes if (hc->PhysMax < PhysMax) hc->PhysMax = PhysMax; if (hc->PhysMin > PhysMin) hc->PhysMin = PhysMin; if (hc->GDFTYP < gdftyp) { /* when data type changes, use the largest data type */ if (4 < hc->GDFTYP && hc->GDFTYP < 9 && gdftyp==16) /* (U)INT32, (U)INT64 + FLOAT32 -> DOUBLE */ hc->GDFTYP = 17; else hc->GDFTYP = gdftyp; } else if (hc->GDFTYP > gdftyp) { /* when data type changes, use the largest data type */ if (4 < gdftyp && gdftyp < 9 && hc->GDFTYP==16) /* (U)INT32, (U)INT64 + FLOAT32 -> DOUBLE */ hc->GDFTYP = 17; } if (fabs(hc->Cal - Cal) > 1e-9*Cal) { /* when scaling changes from sweep to sweep, use floating point numbers internally. */ if (hc->GDFTYP < 5) // int16 or smaller hc->GDFTYP = 16; else if (hc->GDFTYP < 9) // int32, int64 -> double hc->GDFTYP = 17; } if ((pdc & 0xFFE0) != (hc->PhysDimCode & 0xFFE0)) { fprintf(stdout, "ERROR: [%i,%i,%i,%i] Yunits in %s do not match %04x(%s) ! %04x(%s)\n",k1,k2,k3,k4, Label, pdc, PhysDim3(pdc), hc->PhysDimCode, PhysDim3(hc->PhysDimCode)); biosigERROR(hdr, B4C_FORMAT_UNSUPPORTED, "Heka/Patchmaster: Yunits do not match"); } if ( ( VERBOSE_LEVEL > 7 ) && ( fabs( DT[ns] - dT) > 1e-9 * dT) ) { fprintf(stdout, "%s (line %i) different sampling rates [%i,%i,%i,%i]#%i,%f/%f \n",__FILE__,__LINE__,(int)k1,(int)k2,(int)k3,(int)k4,(int)ns, 1.0/DT[ns],1.0/dT); } } if (YOffset) { biosigERROR(hdr, B4C_FORMAT_UNSUPPORTED, "Heka/Patchmaster: YOffset is not zero"); } if (hdr->AS.Header[pos+220] != 1) { fprintf(stderr,"WARNING Heka/Patchmaster: ValidYRange not set to 1 but %i in sweep [%i,%i,%i,%i]\n", hdr->AS.Header[pos+220],k1+1,k2+1,k3+1,k4+1); } if (VERBOSE_LEVEL>7) fprintf(stdout,"HEKA L6 @%i= #%i,%i, %s %f-%fHz\t%i/%i %i/%i %i/%i %i/%i \n",(int)(pos+StartOfData),ns,AdcChan,Label,hdr->SampleRate,Fs,k1,K1,k2,K2,k3,K3,k4,K4); pos += Sizes.Rec.Trace+4; // read number of children -- this should be 0 - ALWAYS; K5 = (*(uint32_t*)(hdr->AS.Header+pos-4)); if (K5) { biosigERROR(hdr, B4C_FORMAT_UNSUPPORTED, "Heka/Patchmaster: Level 4 has some children"); } } // end loop k4 // if sweep is selected, add number of samples to counter if (flagSweepSelected) { if ( hdr->SPR > 0xffffffffffffffffu-SPR) { biosigERROR(hdr,B4C_FORMAT_UNSUPPORTED,"HEKA file has more than 2^32 samples - this is not supported yet"); } hdr->SPR += SPR; } } // end loop k3 } // end loop k2 } // end loop k1 #ifndef NO_BI if (DT) free(DT); #else size_t *BI = (size_t*) DT; // DT is not used anymore, use space for BI #endif DT = NULL; #ifdef WITH_TIMESTAMPCHANNEL { /* define time stamp channel, memory is already allocated above */ CHANNEL_TYPE *hc = hdr->CHANNEL + hdr->NS; hc->GDFTYP = 7; // corresponds to int64_t, gdf_time strcpy(hc->Label,"Timestamp"); hc->Transducer[0]=0; hc->PhysDimCode = 2272; // units: days [d] hc->LeadIdCode = 0; hc->SPR = 1; hc->Cal = ldexp(1.0, -32); hc->Off = 0.0; hc->OnOff = 1; hc->DigMax = ldexp( 1.0, 61); hc->DigMin = 0; hc->PhysMax = hc->DigMax * hc->Cal; hc->PhysMin = hc->DigMin * hc->Cal; hc->TOffset = 0.0; hc->Impedance = NAN; hc->HighPass = NAN; hc->LowPass = NAN; hc->Notch = NAN; hc->XYZ[0] = 0.0; hc->XYZ[1] = 0.0; hc->XYZ[2] = 0.0; hdr->NS++; } #endif hdr->NRec = 1; hdr->AS.bpb = 0; for (k = 0; k < hdr->NS; k++) { CHANNEL_TYPE *hc = hdr->CHANNEL + k; hc->Cal = (hc->PhysMax - hc->PhysMin) / (hc->DigMax - hc->DigMin); hc->Off = hc->PhysMin - hc->DigMin * hc->Cal; #ifndef NO_BI hc->bi = hdr->AS.bpb; #else BI[k] = hdr->AS.bpb; #endif hc->SPR = hdr->SPR; hdr->AS.bpb += hc->SPR * (GDFTYP_BITS[hc->GDFTYP]>>3); // multiplation must not exceed 32 bit limit } if (hdr->AS.B4C_ERRNUM) { #ifdef NO_BI if (BI) free(BI); #endif return; } hdr->ID.Manufacturer.Name = "HEKA/Patchmaster"; /****************************************************************************** SREAD_HEKA void sread_heka(HDRTYPE* hdr, FILE *itx, ... ) { ******************************************************************************/ if (VERBOSE_LEVEL > 7) fprintf(stdout,"HEKA: 400: %"PRIi64" %"PRIi32" %"PRIi64"\n",hdr->NRec, hdr->AS.bpb, hdr->NRec * (size_t)hdr->AS.bpb); size_t sz = hdr->NRec * (size_t)hdr->AS.bpb; if (sz/hdr->NRec < hdr->AS.bpb) { biosigERROR(hdr, B4C_MEMORY_ALLOCATION_FAILED, "memory allocation failed - more than 2GB required but platform supports only 32 bit!"); return; } void* tmpptr = realloc(hdr->AS.rawdata, sz); if (tmpptr!=NULL) hdr->AS.rawdata = (uint8_t*) tmpptr; else { biosigERROR(hdr, B4C_MEMORY_ALLOCATION_FAILED, "memory allocation failed - not enough memory!"); return; } assert(hdr->NRec >= 0); memset(hdr->AS.rawdata, 0xff, hdr->NRec * (size_t)hdr->AS.bpb); // initialize with NAN's #ifdef NO_BI #define _BI (BI[k]) #else #define _BI (hc->bi) #endif /* initialize with NAN's */ for (k=0; k<hdr->NS; k++) { size_t k1; CHANNEL_TYPE *hc = hdr->CHANNEL+k; switch (hc->GDFTYP) { case 3: for (k1=0; k1<hc->SPR; k1++) { *(uint16_t*)(hdr->AS.rawdata + _BI + k1 * 2) = 0x8000; } break; case 5: for (k1=0; k1<hc->SPR; k1++) *(uint32_t*)(hdr->AS.rawdata + _BI + k1 * 4) = 0x80000000; break; case 7: for (k1=0; k1<hc->SPR; k1++) *(int64_t*)(hdr->AS.rawdata + _BI + k1 * 4) = 0x8000000000000000LL; break; case 16: for (k1=0; k1<hc->SPR; k1++) *(float*)(hdr->AS.rawdata + _BI + k1 * 4) = NAN; break; case 17: for (k1=0; k1<hc->SPR; k1++) *(double*)(hdr->AS.rawdata + _BI + k1 * 8) = NAN; break; } } #undef _BI char *WAVENAME = NULL; if (itx) { fprintf(itx, "IGOR\r\nX Silent 1\r\n"); const char *fn = strrchr(hdr->FileName,'\\'); if (fn) fn++; else fn = strrchr(hdr->FileName,'/'); if (fn) fn++; else fn = hdr->FileName; size_t len = strspn(fn,"."); WAVENAME = (char*)malloc(strlen(hdr->FileName)+7); if (len) strncpy(WAVENAME, fn, len); else strcpy(WAVENAME, fn); // Flawfinder: ignore } if (VERBOSE_LEVEL>7) hdr2ascii(hdr,stdout,4); /******************************************************************************************************* HEKA: read data blocks *******************************************************************************************************/ uint32_t SPR = 0; pos = StartOfPulse + Sizes.Rec.Root + 4; for (k1=0; k1<K1; k1++) { // read group if (VERBOSE_LEVEL>7) fprintf(stdout,"HEKA+L1 @%i=\t%i/%i \n",(int)(pos+StartOfData),k1,K1); pos += Sizes.Rec.Group+4; // read number of children K2 = (*(uint32_t*)(hdr->AS.Header+pos-4)); for (k2=0; k2<K2; k2++) { // read series union { double f64; uint64_t u64; } Delay; uint32_t spr = 0; char *SeLabel = (char*)(hdr->AS.Header+pos+4); // max 32 bytes Delay.u64 = bswap_64(*(uint64_t*)(hdr->AS.Header+pos+472+176)); if (VERBOSE_LEVEL>7) fprintf(stdout,"HEKA+L2 @%i=%s %f\t%i/%i %i/%i \n",(int)(pos+StartOfData),SeLabel,Delay.f64,k1,K1,k2,K2); /* move to reading of data */ pos += Sizes.Rec.Series+4; // read number of children K3 = (*(uint32_t*)(hdr->AS.Header+pos-4)); for (k3=0; k3<K3; k3++) { #if defined(WITH_TIMESTAMPCHANNEL) #ifdef NO_BI #define _BI (BI[hdr->NS-1]) #else #define _BI (hdr->CHANNEL[hdr->NS-1].bi) #endif gdf_time t = heka2gdftime(*(double*)(hdr->AS.Header+pos+48)); // time of sweep. TODO: this should be taken into account *(int64_t*)(hdr->AS.rawdata + _BI + SPR * 8) = t; #undef _BI #endif // WITH_TIMESTAMPCHANNEL // read sweep char flagSweepSelected = (hdr->AS.SegSel[0]==0 || k1+1==hdr->AS.SegSel[0]) && (hdr->AS.SegSel[1]==0 || k2+1==hdr->AS.SegSel[1]) && (hdr->AS.SegSel[2]==0 || k3+1==hdr->AS.SegSel[2]); if (VERBOSE_LEVEL>7) fprintf(stdout,"HEKA+L3 @%i=\t%i/%i %i/%i %i/%i sel=%i\n",(int)(pos+StartOfData),k1,K1,k2,K2,k3,K3,flagSweepSelected); pos += Sizes.Rec.Sweep + 4; // read number of children K4 = (*(uint32_t*)(hdr->AS.Header+pos-4)); size_t DIV=1; for (k4=0; k4<K4; k4++) { if (!flagSweepSelected) { pos += Sizes.Rec.Trace+4; continue; } // read trace uint16_t gdftyp = 0; uint32_t ns = (*(uint32_t*)(hdr->AS.Header+pos+36)); uint32_t DataPos = (*(uint32_t*)(hdr->AS.Header+pos+40)); spr = (*(uint32_t*)(hdr->AS.Header+pos+44)); double DataScaler= (*(double*)(hdr->AS.Header+pos+72)); double Toffset = (*(double*)(hdr->AS.Header+pos+80)); // time offset of uint16_t pdc = PhysDimCode((char*)(hdr->AS.Header + pos + 96)); char *physdim = (char*)(hdr->AS.Header + pos + 96); double dT = (*(double*)(hdr->AS.Header+pos+104)); // double XStart = (*(double*)(hdr->AS.Header+pos+112)); // uint16_t XUnits = PhysDimCode((char*)(hdr->AS.Header+pos+120)); double YRange = (*(double*)(hdr->AS.Header+pos+128)); double YOffset = (*(double*)(hdr->AS.Header+pos+136)); // double Bandwidth = (*(double*)(hdr->AS.Header+pos+144)); uint16_t AdcChan = (*(uint16_t*)(hdr->AS.Header+pos+222)); /* double PhysMin = (*(double*)(hdr->AS.Header+pos+224)); double PhysMax = (*(double*)(hdr->AS.Header+pos+232)); */ switch (hdr->AS.Header[pos+70]) { case 0: gdftyp = 3; break; // int16 case 1: gdftyp = 5; break; // int32 case 2: gdftyp = 16; break; // float32 case 3: gdftyp = 17; break; // float64 default: biosigERROR(hdr, B4C_FORMAT_UNSUPPORTED, "Heka/Patchmaster unknown data type is used"); }; if (SWAP) { AdcChan = bswap_16(AdcChan); ns = bswap_32(ns); DataPos = bswap_32(DataPos); spr = bswap_32(spr); // avoid breaking strict-aliasing rules union { double f64; uint64_t u64; } c; c.f64 = dT; c.u64 = bswap_64(c.u64); dT = c.f64; c.f64 = YRange; c.u64 = bswap_64(c.u64); YRange = c.f64; c.f64 = YOffset; c.u64 = bswap_64(c.u64); YOffset = c.f64; /* c.f64 = PhysMax; c.u64 = bswap_64(c.u64); PhysMax = c.f64; c.f64 = PhysMin; c.u64 = bswap_64(c.u64); PhysMin = c.f64; */ c.f64 = Toffset; c.u64 = bswap_64(c.u64); Toffset = c.f64; } double Fs = round(1.0 / dT); DIV = round(hdr->SampleRate / Fs); char *Label = (char*)(hdr->AS.Header+pos+4); for (ns=0; ns < hdr->NS; ns++) { if (!strcmp(hdr->CHANNEL[ns].Label, Label)) break; } CHANNEL_TYPE *hc = hdr->CHANNEL+ns; if (VERBOSE_LEVEL>7) fprintf(stdout,"HEKA+L4 @%i= #%i,%i,%i/%i %s\t%i/%i %i/%i %i/%i %i/%i DIV=%i,%i,%i\n",(int)(pos+StartOfData),ns,AdcChan,spr,SPR,Label,k1,K1,k2,K2,k3,K3,k4,K4,(int)DIV,gdftyp,hc->GDFTYP); if (itx) { uint32_t k5; double Cal = DataScaler; assert(hdr->CHANNEL[ns].Off==0.0); double Off = 0.0; fprintf(itx, "\r\nWAVES %s_%i_%i_%i_%i\r\nBEGIN\r\n", WAVENAME,k1+1,k2+1,k3+1,k4+1); switch (hc->GDFTYP) { case 3: for (k5 = 0; k5 < spr; ++k5) fprintf(itx,"% e\n", (double)*(int16_t*)(hdr->AS.Header + DataPos + k5 * 2) * Cal + Off); break; case 5: for (k5 = 0; k5 < spr; ++k5) fprintf(itx,"% e\n", (double)*(int32_t*)(hdr->AS.Header + DataPos + k5 * 4) * Cal + Off); break; case 16: for (k5 = 0; k5 < spr; ++k5) fprintf(itx,"% e\n", (double)*(float*)(hdr->AS.Header + DataPos + k5 * 4) * Cal + Off); break; case 17: for (k5 = 0; k5 < spr; ++k5) fprintf(itx,"% e\n", *(double*)(hdr->AS.Header + DataPos + k5 * 8) * Cal + Off); break; } fprintf(itx, "END\r\nX SetScale/P x, %g, %g, \"s\", %s_%i_%i_%i_%i\r\n", Toffset, dT, WAVENAME, k1+1,k2+1,k3+1,k4+1); fprintf(itx, "X SetScale y,0,0,\"%s\", %s_%i_%i_%i_%i\n", physdim, WAVENAME, k1+1,k2+1,k3+1,k4+1); } #ifdef NO_BI #define _BI (BI[ns]) #else #define _BI (hc->bi) #endif // no need to check byte order because File.Endian is set and endian conversion is done in sread if ((DIV==1) && (gdftyp == hc->GDFTYP)) { uint16_t sz = GDFTYP_BITS[hc->GDFTYP]>>3; memcpy(hdr->AS.rawdata + _BI + SPR * sz, hdr->AS.Header + DataPos, spr * sz); } else if (1) { double Cal = DataScaler * PhysDimScale(pdc) / hdr->CHANNEL[ns].Cal; assert(Cal==1.0 || hc->GDFTYP > 15); // when scaling changes, target data type is always float/double -> see above uint32_t k5,k6; switch (gdftyp) { case 3: switch (hc->GDFTYP) { case 3: for (k5 = 0; k5 < spr; ++k5) { int16_t ival = *(int16_t*)(hdr->AS.Header + DataPos + k5 * 2); for (k6 = 0; k6 < DIV; ++k6) *(int16_t*)(hdr->AS.rawdata + _BI + (SPR + k5*DIV + k6) * 2) = ival; } break; case 5: for (k5 = 0; k5 < spr; ++k5) { int16_t ival = *(int16_t*)(hdr->AS.Header + DataPos + k5 * 2); for (k6 = 0; k6 < DIV; ++k6) *(int32_t*)(hdr->AS.rawdata + _BI + (SPR + k5*DIV + k6) * 4) = (int32_t)ival; } break; case 16: for (k5 = 0; k5 < spr; ++k5) { int16_t ival = *(int16_t*)(hdr->AS.Header + DataPos + k5 * 2); for (k6 = 0; k6 < DIV; ++k6) *(float*)(hdr->AS.rawdata + _BI + (SPR + k5*DIV + k6) * 4) = (float)ival * Cal; } break; case 17: for (k5 = 0; k5 < spr; ++k5) { int16_t ival = *(int16_t*)(hdr->AS.Header + DataPos + k5 * 2); for (k6 = 0; k6 < DIV; ++k6) *(double*)(hdr->AS.rawdata + _BI + (SPR + k5*DIV + k6) * 8) = (double)ival * Cal; } break; } break; case 5: switch (hc->GDFTYP) { case 5: for (k5 = 0; k5 < spr; ++k5) { int32_t ival = *(int32_t*)(hdr->AS.Header + DataPos + k5 * 4); for (k6 = 0; k6 < DIV; ++k6) *(int32_t*)(hdr->AS.rawdata + _BI + (SPR + k5*DIV + k6) * 4) = ival; } break; case 16: for (k5 = 0; k5 < spr; ++k5) { int32_t ival = *(int32_t*)(hdr->AS.Header + DataPos + k5 * 4); for (k6 = 0; k6 < DIV; ++k6) *(float*)(hdr->AS.rawdata + _BI + (SPR + k5*DIV + k6) * 4) = (float)ival * Cal; } break; case 17: for (k5 = 0; k5 < spr; ++k5) { int32_t ival = *(int32_t*)(hdr->AS.Header + DataPos + k5 * 4); for (k6 = 0; k6 < DIV; ++k6) *(double*)(hdr->AS.rawdata + _BI + (SPR + k5*DIV + k6) * 8) = (double)ival * Cal; } break; } break; case 16: switch (hc->GDFTYP) { case 16: for (k5 = 0; k5 < spr; ++k5) { float ival = *(float*)(hdr->AS.Header + DataPos + k5 * 4); for (k6 = 0; k6 < DIV; ++k6) *(float*)(hdr->AS.rawdata + _BI + (SPR + k5*DIV + k6) * 4) = ival * Cal; } break; case 17: for (k5 = 0; k5 < spr; ++k5) { float ival = *(float*)(hdr->AS.Header + DataPos + k5 * 4); for (k6 = 0; k6 < DIV; ++k6) *(double*)(hdr->AS.rawdata + _BI + (SPR + k5*DIV + k6) * 8) = (double)ival * Cal; } break; } break; case 17: switch (hc->GDFTYP) { case 17: for (k5 = 0; k5 < spr; ++k5) { double ival = *(double*)(hdr->AS.Header + DataPos + k5 * 8); for (k6 = 0; k6 < DIV; ++k6) *(double*)(hdr->AS.rawdata + _BI + (SPR + k5*DIV + k6) * 8) = ival * Cal; } break; } break; } } #undef _BI pos += Sizes.Rec.Trace+4; } if (flagSweepSelected) SPR += spr * DIV; } }