Example #1
0
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;
}
Example #2
0
void mexFunction(
    int           nlhs,           /* number of expected outputs */
    mxArray       *plhs[],        /* array of pointers to output arguments */
    int           nrhs,           /* number of inputs */
    const mxArray *prhs[]         /* array of pointers to input arguments */
)

{
	int k,k1;
	const mxArray	*arg;
	mxArray		*HDR;
	HDRTYPE		*hdr;
	CHANNEL_TYPE*	cp; 
	size_t 		count;
	time_t 		T0;
	char 		*FileName=NULL;  
	int 		status; 
	int		CHAN = 0;
	int		TARGETSEGMENT = 1; 
	double		*ChanList=NULL;
	int		NS = -1;
	char		FlagOverflowDetection = 1, FlagUCAL = 0;
	char		FLAG_CNT32 = 0;
	int		argSweepSel = -1;
	
#ifdef CHOLMOD_H
	cholmod_sparse RR,*rr=NULL;
	double dummy;
#endif 

// ToDO: output single data 
//	mxClassId	FlagMXclass=mxDOUBLE_CLASS;
	

	if (nrhs<1) {
#ifdef mexSOPEN
		mexPrintf("   Usage of mexSOPEN:\n");
		mexPrintf("\tHDR = mexSOPEN(f)\n");
		mexPrintf("   Input:\n\tf\tfilename\n");
		mexPrintf("   Output:\n\tHDR\theader structure\n\n");
#else
		mexPrintf("   Usage of mexSLOAD:\n");
		mexPrintf("\t[s,HDR]=mexSLOAD(f)\n");
		mexPrintf("\t[s,HDR]=mexSLOAD(f,chan)\n\t\tchan must be sorted in ascending order\n");
		mexPrintf("\t[s,HDR]=mexSLOAD(f,ReRef)\n\t\treref is a (sparse) matrix for rerefencing\n");
		mexPrintf("\t[s,HDR]=mexSLOAD(f,chan,'...')\n");
		mexPrintf("\t[s,HDR]=mexSLOAD(f,chan,'CNT32')\n");
		mexPrintf("\t[s,HDR]=mexSLOAD(f,chan,'OVERFLOWDETECTION:ON')\n");
		mexPrintf("\t[s,HDR]=mexSLOAD(f,chan,'OVERFLOWDETECTION:OFF')\n");
		mexPrintf("\t[s,HDR]=mexSLOAD(f,chan,'UCAL:ON')\n");
		mexPrintf("\t[s,HDR]=mexSLOAD(f,chan,'UCAL:OFF')\n");
		mexPrintf("\t[s,HDR]=mexSLOAD(f,chan,'OUTPUT:SINGLE')\n");
		mexPrintf("\t[s,HDR]=mexSLOAD(f,chan,'TARGETSEGMENT:<N>')\n");
		mexPrintf("\t[s,HDR]=mexSLOAD(f,chan,'SWEEP',[NE, NG, NS])\n");
		mexPrintf("   Input:\n\tf\tfilename\n");
		mexPrintf("\tchan\tlist of selected channels; 0=all channels [default]\n");
		mexPrintf("\tCNT32: needed to read 32bit CNT files \n");
		mexPrintf("\tUCAL\tON: do not calibrate data; default=OFF\n");
//		mexPrintf("\tOUTPUT\tSINGLE: single precision; default='double'\n");
		mexPrintf("\tOVERFLOWDETECTION\tdefault = ON\n\t\tON: values outside dynamic range are not-a-number (NaN)\n");
		mexPrintf("\tTARGETSEGMENT:<N>\n\t\tselect segment <N> in multisegment files (like Nihon-Khoden), default=1\n\t\tIt has no effect for other data formats.\n");
		mexPrintf("\t[NE, NG, NS] are the number of the experiment, the series and the sweep, resp. for sweep selection in HEKA/PatchMaster files. (0 indicates all)\n");
		mexPrintf("\t\t examples: [1,2,3] the 3rd sweep from the 2nd series of experiment 1; [1,3,0] selects all sweeps from experiment=1, series=3. \n\n");
		mexPrintf("   Output:\n\ts\tsignal data, each column is one channel\n");
		mexPrintf("\tHDR\theader structure\n\n");
#endif
		return; 
	}

/*
 	improve checks for input arguments
*/
	/* process input arguments */
	for (k = 0; k < nrhs; k++) {	
		arg = prhs[k];
		if (mxIsEmpty(arg) && (k>0)) {
#ifdef DEBUG		
			mexPrintf("arg[%i] Empty\n",k);
#endif
		}
		else if ((k==0) && mxIsCell(arg) && mxGetNumberOfElements(arg)==1 && mxGetCell(arg,0) && mxIsChar(mxGetCell(arg,0))) {
			FileName = mxArrayToString(mxGetCell(arg,0));
#ifdef DEBUG		
			mexPrintf("arg[%i] IsCell\n",k);
#endif
		}
		else if ((k==0) && mxIsStruct(arg)) {
			FileName = mxArrayToString(mxGetField(prhs[k],0,"FileName"));
#ifdef DEBUG		
			mexPrintf("arg[%i] IsStruct\n",k);
#endif
		}
#ifdef CHOLMOD_H
		else if ((k==1) && mxIsSparse(arg)) {
			rr = sputil_get_sparse(arg,&RR,&dummy,0);
		}
#endif 
		else if ((k==1) && mxIsNumeric(arg)) {
#ifdef DEBUG		
			mexPrintf("arg[%i] IsNumeric\n",k);
#endif
			ChanList = (double*)mxGetData(prhs[k]);
			NS = mxGetNumberOfElements(prhs[k]);
		}	
		else if (mxIsChar(arg)) {
#ifdef DEBUG		
			mexPrintf("arg[%i]=%s \n",k,mxArrayToString(prhs[k]));
#endif
			if (k==0)			
				FileName = mxArrayToString(prhs[k]);
			else if (!strcmp(mxArrayToString(prhs[k]), "CNT32"))
				FLAG_CNT32 = 1;
			else if (!strcmp(mxArrayToString(prhs[k]), "OVERFLOWDETECTION:ON"))
				FlagOverflowDetection = 1;
			else if (!strcmp(mxArrayToString(prhs[k]), "OVERFLOWDETECTION:OFF"))
				FlagOverflowDetection = 0;
			else if (!strcmp(mxArrayToString(prhs[k]), "UCAL:ON")) 
				FlagUCAL = 1;
			else if (!strcmp(mxArrayToString(prhs[k]), "UCAL:OFF"))
				FlagUCAL = 0;
//			else if (!strcmp(mxArrayToString(prhs[k]),"OUTPUT:SINGLE"))
//				FlagMXclass = mxSINGLE_CLASS;
			else if (!strncmp(mxArrayToString(prhs[k]),"TARGETSEGMENT:",14))
				TARGETSEGMENT = atoi(mxArrayToString(prhs[k])+14);
			else if (!strcmpi(mxArrayToString(prhs[k]), "SWEEP") && (prhs[k+1] != NULL) && mxIsNumeric(prhs[k+1]))
				argSweepSel = ++k;
		}
		else {
#ifndef mexSOPEN
			mexPrintf("mexSLOAD: argument #%i is invalid.",k+1);	
			mexErrMsgTxt("mexSLOAD failes because of unknown parameter\n");	
#else
			mexPrintf("mexSOPEN: argument #%i is invalid.",k+1);	
			mexErrMsgTxt("mexSOPEN fails because of unknown parameter\n");	
#endif
		}
	}

	if (VERBOSE_LEVEL>7) 
		mexPrintf("110: input arguments checked\n");

	hdr = constructHDR(0,0);
	hdr->FLAG.OVERFLOWDETECTION = FlagOverflowDetection; 
	hdr->FLAG.UCAL = FlagUCAL;
	hdr->FLAG.CNT32 = FLAG_CNT32;
#ifdef CHOLMOD_H
	hdr->FLAG.ROW_BASED_CHANNELS = (rr!=NULL); 
#else 	
	hdr->FLAG.ROW_BASED_CHANNELS = 0; 
#endif 
	hdr->FLAG.TARGETSEGMENT = TARGETSEGMENT;

	// sweep selection for Heka format 
	if (argSweepSel>0) { 				
		double *SZ     = (double*) mxGetData(prhs[argSweepSel]);
		k = 0;
		while (k < mxGetNumberOfElements(prhs[argSweepSel]) && k < 5) { 
			hdr->AS.SegSel[k] = (uint32_t)SZ[k];
			k++;
		}
	}

	if (VERBOSE_LEVEL>7) 
		mexPrintf("120: going to sopen\n");

	hdr = sopen(FileName, "r", hdr);
/*
#ifdef WITH_PDP 
	if (B4C_ERRNUM) {
		B4C_ERRNUM = 0;
		sopen_pdp_read(hdr);
	}	
#endif
*/
	if (VERBOSE_LEVEL>7) 
		mexPrintf("121: sopen done\n");

	if ((status=serror())) {

		const char* fields[]={"TYPE","VERSION","FileName","FLAG","ErrNum","ErrMsg"};
		HDR = mxCreateStructMatrix(1, 1, 6, fields);
		mxSetField(HDR,0,"FileName",mxCreateString(hdr->FileName));
		mxArray *errnum = mxCreateNumericMatrix(1,1,mxUINT8_CLASS,mxREAL);
		*(uint8_t*)mxGetData(errnum) = (uint8_t)status;
		mxSetField(HDR,0,"ErrNum",errnum);
		
#ifdef HAVE_OCTAVE
		// handle bug in octave: mxCreateString(NULL) causes segmentation fault
		// Octave 3.2.3 causes a seg-fault in mxCreateString(NULL)
		 
		{
		const char *p = GetFileTypeString(hdr->TYPE);
		if (p) mxSetField(HDR,0,"TYPE",mxCreateString(p));
		}
#else
		mxSetField(HDR,0,"TYPE",mxCreateString(GetFileTypeString(hdr->TYPE)));
#endif
		mxSetField(HDR,0,"VERSION",mxCreateDoubleScalar(hdr->VERSION));

		char msg[1024]; 
		if (status==B4C_CANNOT_OPEN_FILE)
			sprintf(msg,"Error mexSLOAD: file %s not found.\n",FileName);
		else if (status==B4C_FORMAT_UNKNOWN)
			sprintf(msg,"Error mexSLOAD: Cannot open file %s - format %s not known.\n",FileName,GetFileTypeString(hdr->TYPE));
		else if (status==B4C_FORMAT_UNSUPPORTED)
			sprintf(msg,"Error mexSLOAD: Cannot open file %s - format %s not supported [%s].\n", FileName, GetFileTypeString(hdr->TYPE), B4C_ERRMSG);
		else 	
			sprintf(msg,"Error %i mexSLOAD: Cannot open file %s - format %s not supported [%s].\n", status, FileName, GetFileTypeString(hdr->TYPE), B4C_ERRMSG);
			
		mxSetField(HDR,0,"ErrMsg",mxCreateString(msg));

	if (VERBOSE_LEVEL>7) 
		mexPrintf("737: abort mexSLOAD - sopen failed\n");


		destructHDR(hdr);

	if (VERBOSE_LEVEL>7) 
		mexPrintf("757: abort mexSLOAD - sopen failed\n");

#ifdef mexSOPEN
		plhs[0] = HDR; 
#else
		plhs[0] = mxCreateDoubleMatrix(0,0, mxREAL);
		plhs[1] = HDR; 
#endif 		 
	if (VERBOSE_LEVEL>7) 
		mexPrintf("777: abort mexSLOAD - sopen failed\n");

		return; 
	}

#ifdef CHOLMOD_H
	RerefCHANNEL(hdr,rr,2);
#endif

	if (hdr->FLAG.OVERFLOWDETECTION != FlagOverflowDetection)
		mexPrintf("Warning mexSLOAD: Overflowdetection not supported in file %s\n",hdr->FileName);
	if (hdr->FLAG.UCAL != FlagUCAL)
		mexPrintf("Warning mexSLOAD: Flag UCAL is %i instead of %i (%s)\n",hdr->FLAG.UCAL,FlagUCAL,hdr->FileName);


	if (VERBOSE_LEVEL>7) 
		fprintf(stderr,"[112] SOPEN-R finished NS=%i %i\n",hdr->NS,NS);

//	convert2to4_eventtable(hdr); 
		
#ifdef CHOLMOD_H
	if (hdr->Calib != NULL) {
		NS = hdr->Calib->ncol;
	}
	else 
#endif
	if ((NS<0) || ((NS==1) && (ChanList[0] == 0.0))) { 	// all channels
		for (k=0, NS=0; k<hdr->NS; ++k) {
			if (hdr->CHANNEL[k].OnOff) NS++; 
		}	
	}		
	else {		
		for (k=0; k<hdr->NS; ++k)
			hdr->CHANNEL[k].OnOff = 0; 	// reset
		for (k=0; k<NS; ++k) {
			int ch = (int)ChanList[k];
			if ((ch < 1) || (ch > hdr->NS)) 
				mexPrintf("Invalid channel number CHAN(%i) = %i!\n",k+1,ch); 
			else 	
				hdr->CHANNEL[ch-1].OnOff = 1;  // set
		}		
	}
	
	if (VERBOSE_LEVEL>7) 
		fprintf(stderr,"[113] NS=%i %i\n",hdr->NS,NS);

#ifndef mexSOPEN
	if (hdr->FLAG.ROW_BASED_CHANNELS)
		plhs[0] = mxCreateDoubleMatrix(NS, hdr->NRec*hdr->SPR, mxREAL);
	else
		plhs[0] = mxCreateDoubleMatrix(hdr->NRec*hdr->SPR, NS, mxREAL);

	count = sread(mxGetPr(plhs[0]), 0, hdr->NRec, hdr);
	hdr->NRec = count; 
#endif
	sclose(hdr);
#ifdef CHOLMOD_H
        if (hdr->Calib && hdr->rerefCHANNEL) {
		hdr->NS = hdr->Calib->ncol; 
                free(hdr->CHANNEL);
                hdr->CHANNEL = hdr->rerefCHANNEL;
                hdr->rerefCHANNEL = NULL; 
                hdr->Calib = NULL; 
        }                
#endif 
	if ((status=serror())) return;  

	if (VERBOSE_LEVEL>7) 
		fprintf(stderr,"\n[129] SREAD/SCLOSE on %s successful [%i,%i] [%Li,%i] %i.\n",hdr->FileName,hdr->data.size[0],hdr->data.size[1],hdr->NRec,count,NS);


//	hdr2ascii(hdr,stderr,4);	

#ifndef mexSOPEN 

	if (nlhs>1) { 
#endif

		char* mexFileName = (char*)mxMalloc(strlen(hdr->FileName)+1); 

		mxArray *tmp, *tmp2, *Patient, *Manufacturer, *ID, *EVENT, *Filter, *Flag, *FileType;
		uint16_t numfields;
		const char *fnames[] = {"TYPE","VERSION","FileName","T0","FILE","Patient",\
		"HeadLen","NS","SPR","NRec","SampleRate", "FLAG", \
		"EVENT","Label","LeadIdCode","PhysDimCode","PhysDim","Filter",\
		"PhysMax","PhysMin","DigMax","DigMin","Transducer","Cal","Off","GDFTYP","TOffset",\
		"LowPass","HighPass","Notch","ELEC","Impedance","fZ","AS","Dur","REC","Manufacturer",NULL};

		for (numfields=0; fnames[numfields++] != NULL; );
		HDR = mxCreateStructMatrix(1, 1, --numfields, fnames);

		mxSetField(HDR,0,"TYPE",mxCreateString(GetFileTypeString(hdr->TYPE)));
		mxSetField(HDR,0,"HeadLen",mxCreateDoubleScalar(hdr->HeadLen));
		mxSetField(HDR,0,"VERSION",mxCreateDoubleScalar(hdr->VERSION));
		mxSetField(HDR,0,"NS",mxCreateDoubleScalar(hdr->NS));
		mxSetField(HDR,0,"SPR",mxCreateDoubleScalar(hdr->SPR));
		mxSetField(HDR,0,"NRec",mxCreateDoubleScalar(hdr->NRec));
		mxSetField(HDR,0,"SampleRate",mxCreateDoubleScalar(hdr->SampleRate));
		mxSetField(HDR,0,"Dur",mxCreateDoubleScalar(hdr->SPR/hdr->SampleRate));
		mxSetField(HDR,0,"FileName",mxCreateCharMatrixFromStrings(1,&hdr->FileName));
                
		mxSetField(HDR,0,"T0",mxCreateDoubleScalar(ldexp(hdr->T0,-32)));

		/* Channel information */ 
#ifdef CHOLMOD_H
/*
        	if (hdr->Calib == NULL) { // is refering to &RR, do not destroy
		        mxArray *Calib = mxCreateDoubleMatrix(hdr->Calib->nrow, hdr->Calib->ncol, mxREAL);

        	}
*/
#endif
		mxArray *LeadIdCode  = mxCreateDoubleMatrix(1,hdr->NS, mxREAL);
		mxArray *PhysDimCode = mxCreateDoubleMatrix(1,hdr->NS, mxREAL);
		mxArray *GDFTYP      = mxCreateDoubleMatrix(1,hdr->NS, mxREAL);
		mxArray *PhysMax     = mxCreateDoubleMatrix(1,hdr->NS, mxREAL);
		mxArray *PhysMin     = mxCreateDoubleMatrix(1,hdr->NS, mxREAL);
		mxArray *DigMax      = mxCreateDoubleMatrix(1,hdr->NS, mxREAL);
		mxArray *DigMin      = mxCreateDoubleMatrix(1,hdr->NS, mxREAL);
		mxArray *Cal         = mxCreateDoubleMatrix(1,hdr->NS, mxREAL);
		mxArray *Off         = mxCreateDoubleMatrix(1,hdr->NS, mxREAL);
		mxArray *Toffset     = mxCreateDoubleMatrix(1,hdr->NS, mxREAL);
		mxArray *ELEC_POS    = mxCreateDoubleMatrix(hdr->NS,3, mxREAL);
		mxArray *ELEC_Orient = mxCreateDoubleMatrix(hdr->NS,3, mxREAL);
		mxArray *ELEC_Area   = mxCreateDoubleMatrix(hdr->NS,1, mxREAL);
		mxArray *LowPass     = mxCreateDoubleMatrix(1,hdr->NS, mxREAL);
		mxArray *HighPass    = mxCreateDoubleMatrix(1,hdr->NS, mxREAL);
		mxArray *Notch       = mxCreateDoubleMatrix(1,hdr->NS, mxREAL);
		mxArray *Impedance   = mxCreateDoubleMatrix(1,hdr->NS, mxREAL);
		mxArray *fZ          = mxCreateDoubleMatrix(1,hdr->NS, mxREAL);
		mxArray *SPR         = mxCreateDoubleMatrix(1,hdr->NS, mxREAL);
		mxArray *Label       = mxCreateCellMatrix(hdr->NS,1);
		mxArray *Transducer  = mxCreateCellMatrix(hdr->NS,1);
		mxArray *PhysDim1    = mxCreateCellMatrix(hdr->NS,1);

		for (size_t k=0; k<hdr->NS; ++k) {
			*(mxGetPr(LeadIdCode)+k)  = (double)hdr->CHANNEL[k].LeadIdCode;
			*(mxGetPr(PhysDimCode)+k) = (double)hdr->CHANNEL[k].PhysDimCode;
			*(mxGetPr(GDFTYP)+k) 	  = (double)hdr->CHANNEL[k].GDFTYP;
			*(mxGetPr(PhysMax)+k) 	  = (double)hdr->CHANNEL[k].PhysMax;
			*(mxGetPr(PhysMin)+k) 	  = (double)hdr->CHANNEL[k].PhysMin;
			*(mxGetPr(DigMax)+k) 	  = (double)hdr->CHANNEL[k].DigMax;
			*(mxGetPr(DigMin)+k) 	  = (double)hdr->CHANNEL[k].DigMin;
			*(mxGetPr(Toffset)+k) 	  = (double)hdr->CHANNEL[k].TOffset;
			*(mxGetPr(Cal)+k) 	  = (double)hdr->CHANNEL[k].Cal;
			*(mxGetPr(Off)+k) 	  = (double)hdr->CHANNEL[k].Off;
			*(mxGetPr(SPR)+k) 	  = (double)hdr->CHANNEL[k].SPR;
			*(mxGetPr(LowPass)+k) 	  = (double)hdr->CHANNEL[k].LowPass;
			*(mxGetPr(HighPass)+k) 	  = (double)hdr->CHANNEL[k].HighPass;
			*(mxGetPr(Notch)+k) 	  = (double)hdr->CHANNEL[k].Notch;
			*(mxGetPr(Impedance)+k)	  = (double)hdr->CHANNEL[k].Impedance;
			*(mxGetPr(fZ)+k)	  = (double)hdr->CHANNEL[k].fZ;
			*(mxGetPr(ELEC_POS)+k) 	  = (double)hdr->CHANNEL[k].XYZ[0];
			*(mxGetPr(ELEC_POS)+k+hdr->NS) 	  = (double)hdr->CHANNEL[k].XYZ[1];
			*(mxGetPr(ELEC_POS)+k+hdr->NS*2)  = (double)hdr->CHANNEL[k].XYZ[2];
/*
			*(mxGetPr(ELEC_Orient)+k) 	  = (double)hdr->CHANNEL[k].Orientation[0];
			*(mxGetPr(ELEC_Orient)+k+hdr->NS) 	  = (double)hdr->CHANNEL[k].Orientation[1];
			*(mxGetPr(ELEC_Orient)+k+hdr->NS*2)  = (double)hdr->CHANNEL[k].Orientation[2];
			*(mxGetPr(ELEC_Area)+k) 	  = (double)hdr->CHANNEL[k].Area;
*/
			mxSetCell(Label,k,mxCreateString(hdr->CHANNEL[k].Label));
			mxSetCell(Transducer,k,mxCreateString(hdr->CHANNEL[k].Transducer));
			
			char p[MAX_LENGTH_PHYSDIM+1];
			PhysDim(hdr->CHANNEL[k].PhysDimCode,p);			
			mxSetCell(PhysDim1,k,mxCreateString(p));
		} 

		mxSetField(HDR,0,"LeadIdCode",LeadIdCode);
		mxSetField(HDR,0,"PhysDimCode",PhysDimCode);
		mxSetField(HDR,0,"GDFTYP",GDFTYP);
		mxSetField(HDR,0,"PhysMax",PhysMax);
		mxSetField(HDR,0,"PhysMin",PhysMin);
		mxSetField(HDR,0,"DigMax",DigMax);
		mxSetField(HDR,0,"DigMin",DigMin);
		mxSetField(HDR,0,"TOffset",Toffset);
		mxSetField(HDR,0,"Cal",Cal);
		mxSetField(HDR,0,"Off",Off);
		mxSetField(HDR,0,"Impedance",Impedance);
		mxSetField(HDR,0,"fZ",fZ);
		mxSetField(HDR,0,"Off",Off);
		mxSetField(HDR,0,"PhysDim",PhysDim1);
		mxSetField(HDR,0,"Transducer",Transducer);
		mxSetField(HDR,0,"Label",Label);

		const char* field[] = {"XYZ","Orientation","Area","GND","REF",NULL};
		for (numfields=0; field[numfields++] != 0; );
		tmp = mxCreateStructMatrix(1, 1, --numfields, field);
		mxSetField(tmp,0,"XYZ",ELEC_POS);
		mxSetField(tmp,0,"Orientation",ELEC_Orient);
		mxSetField(tmp,0,"Area",ELEC_Area);
		mxSetField(HDR,0,"ELEC",tmp);

		const char* field2[] = {"SPR",NULL};
		for (numfields=0; field2[numfields++] != 0; );
		tmp2 = mxCreateStructMatrix(1, 1, --numfields, field2);
		mxSetField(tmp2,0,"SPR",SPR);
		if (hdr->AS.bci2000!=NULL) {
			mxAddField(tmp2, "BCI2000");
			mxSetField(tmp2,0,"BCI2000",mxCreateString(hdr->AS.bci2000));
		}
		if (hdr->TYPE==Sigma) {	
			mxAddField(tmp2, "H1");
			mxSetField(tmp2,0,"H1",mxCreateString((char*)hdr->AS.Header));
		}	
		mxSetField(HDR,0,"AS",tmp2);
				
		/* FLAG */
		const char* field3[] = {"UCAL","OVERFLOWDETECTION","ROW_BASED_CHANNELS",NULL};
		for (numfields=0; field3[numfields++] != 0; );
		Flag = mxCreateStructMatrix(1, 1, --numfields, field3);
#ifdef MX_API_VER
                // Matlab 
       		mxSetField(Flag,0,"UCAL",mxCreateLogicalScalar(hdr->FLAG.UCAL));
        	mxSetField(Flag,0,"OVERFLOWDETECTION",mxCreateLogicalScalar(hdr->FLAG.OVERFLOWDETECTION));
        	mxSetField(Flag,0,"ROW_BASED_CHANNELS",mxCreateLogicalScalar(hdr->FLAG.ROW_BASED_CHANNELS));
#else 
                // mxCreateLogicalScalar are not included in Octave 3.0 
	        mxSetField(Flag,0,"UCAL",mxCreateDoubleScalar(hdr->FLAG.UCAL));
       		mxSetField(Flag,0,"OVERFLOWDETECTION",mxCreateDoubleScalar(hdr->FLAG.OVERFLOWDETECTION));
       		mxSetField(Flag,0,"ROW_BASED_CHANNELS",mxCreateDoubleScalar(hdr->FLAG.ROW_BASED_CHANNELS));
#endif
		mxSetField(HDR,0,"FLAG",Flag);

		/* Filter */ 
		const char *filter_fields[] = {"HighPass","LowPass","Notch",NULL};
		for (numfields=0; filter_fields[numfields++] != 0; );
		Filter = mxCreateStructMatrix(1, 1, --numfields, filter_fields);
		mxSetField(Filter,0,"LowPass",LowPass);
		mxSetField(Filter,0,"HighPass",HighPass);
		mxSetField(Filter,0,"Notch",Notch);
		mxSetField(HDR,0,"Filter",Filter);

		/* annotation, marker, event table */
		const char *event_fields[] = {"SampleRate","TYP","POS","DUR","CHN","Desc",NULL};
		
		if (hdr->EVENT.DUR == NULL)
			EVENT = mxCreateStructMatrix(1, 1, 3, event_fields);
		else {	
			EVENT = mxCreateStructMatrix(1, 1, 5, event_fields);
			mxArray *DUR = mxCreateDoubleMatrix(hdr->EVENT.N,1, mxREAL);
			mxArray *CHN = mxCreateDoubleMatrix(hdr->EVENT.N,1, mxREAL);
			for (size_t k=0; k<hdr->EVENT.N; ++k) {
				*(mxGetPr(DUR)+k) = (double)hdr->EVENT.DUR[k];
				*(mxGetPr(CHN)+k) = (double)hdr->EVENT.CHN[k];  // channels use a 1-based index, 0 indicates all channels
			} 
			mxSetField(EVENT,0,"DUR",DUR);
			mxSetField(EVENT,0,"CHN",CHN);
		}

		if (hdr->EVENT.CodeDesc != NULL) {
			mxAddField(EVENT, "CodeDesc");
			mxArray *CodeDesc = mxCreateCellMatrix(hdr->EVENT.LenCodeDesc-1,1);
			for (size_t k=1; k < hdr->EVENT.LenCodeDesc; ++k) {
				mxSetCell(CodeDesc,k-1,mxCreateString(hdr->EVENT.CodeDesc[k]));
			} 
			mxSetField(EVENT,0,"CodeDesc",CodeDesc);
		}	

		mxArray *TYP = mxCreateDoubleMatrix(hdr->EVENT.N,1, mxREAL);
		mxArray *POS = mxCreateDoubleMatrix(hdr->EVENT.N,1, mxREAL);
		for (size_t k=0; k<hdr->EVENT.N; ++k) {
			*(mxGetPr(TYP)+k) = (double)hdr->EVENT.TYP[k];
			*(mxGetPr(POS)+k) = (double)hdr->EVENT.POS[k]+1;   // conversion from 0-based to 1-based indexing 
		} 
		mxSetField(EVENT,0,"TYP",TYP);
		mxSetField(EVENT,0,"POS",POS);
		mxSetField(EVENT,0,"SampleRate",mxCreateDoubleScalar(hdr->EVENT.SampleRate));
		mxSetField(HDR,0,"EVENT",EVENT);

		/* Record identification */ 
		const char *ID_fields[] = {"Recording","Technician","Hospital","Equipment","IPaddr",NULL};
		for (numfields=0; ID_fields[numfields++] != 0; );
		ID = mxCreateStructMatrix(1, 1, --numfields, ID_fields);
		mxSetField(ID,0,"Recording",mxCreateString(hdr->ID.Recording));
		mxSetField(ID,0,"Technician",mxCreateString(hdr->ID.Technician));
		mxSetField(ID,0,"Hospital",mxCreateString(hdr->ID.Hospital));
		mxSetField(ID,0,"Equipment",mxCreateString((char*)&hdr->ID.Equipment));
		int len = 4; 
		uint8_t IPv6=0;
		for (uint8_t k=4; k<16; k++) IPv6 |= hdr->IPaddr[k];
		if (IPv6) len=16; 
		mxArray *IPaddr = mxCreateNumericMatrix(1,len,mxUINT8_CLASS,mxREAL);
		memcpy(mxGetData(IPaddr),hdr->IPaddr,len);
		mxSetField(ID,0,"IPaddr",IPaddr); 
		mxSetField(HDR,0,"REC",ID);

		/* Patient Information */ 
		const char *patient_fields[] = {"Sex","Handedness","Id","Name","Weight","Height","Birthday",NULL};
		for (numfields=0; patient_fields[numfields++] != 0; );
		Patient = mxCreateStructMatrix(1, 1, --numfields, patient_fields);
		const char *strarray[1];
		if (hdr->Patient.Name) {
			strarray[0] = hdr->Patient.Name; 
			mxSetField(Patient,0,"Name",mxCreateCharMatrixFromStrings(1,strarray));
		}	
		if (hdr->Patient.Id) {
			strarray[0] = hdr->Patient.Id; 
			mxSetField(Patient,0,"Id",mxCreateCharMatrixFromStrings(1,strarray));
		}	
		mxSetField(Patient,0,"Handedness",mxCreateDoubleScalar(hdr->Patient.Handedness));

		mxSetField(Patient,0,"Sex",mxCreateDoubleScalar(hdr->Patient.Sex));
		mxSetField(Patient,0,"Weight",mxCreateDoubleScalar((double)hdr->Patient.Weight));
		mxSetField(Patient,0,"Height",mxCreateDoubleScalar((double)hdr->Patient.Height));
		mxSetField(Patient,0,"Birthday",mxCreateDoubleScalar(ldexp(hdr->Patient.Birthday,-32)));

		double d;
		if (hdr->Patient.Weight==0)		d = 0.0/0.0;	// not-a-number		
		else if (hdr->Patient.Weight==255)	d = 1.0/0.0;	// Overflow
		else					d = (double)hdr->Patient.Weight;
		mxSetField(Patient,0,"Weight",mxCreateDoubleScalar(d));
			
		if (hdr->Patient.Height==0)		d = 0.0/0.0;	// not-a-number		
		else if (hdr->Patient.Height==255)	d = 1.0/0.0;	// Overflow
		else					d = (double)hdr->Patient.Height;
		mxSetField(Patient,0,"Height",mxCreateDoubleScalar(d));
	
		/* Manufacturer Information */ 
		const char *manufacturer_fields[] = {"Name","Model","Version","SerialNumber",NULL};
		for (numfields=0; manufacturer_fields[numfields++] != 0; );
		Manufacturer = mxCreateStructMatrix(1, 1, --numfields, manufacturer_fields);
		if (hdr->ID.Manufacturer.Name) {
			strarray[0] = hdr->ID.Manufacturer.Name;
			mxSetField(Manufacturer,0,"Name",mxCreateCharMatrixFromStrings(1,strarray));
		}	
		if (hdr->ID.Manufacturer.Model) {
			strarray[0] = hdr->ID.Manufacturer.Model;
			mxSetField(Manufacturer,0,"Model",mxCreateCharMatrixFromStrings(1,strarray));
		}	
		if (hdr->ID.Manufacturer.Version) {
			strarray[0] = hdr->ID.Manufacturer.Version;
			mxSetField(Manufacturer,0,"Version",mxCreateCharMatrixFromStrings(1,strarray));
		}	
		if (hdr->ID.Manufacturer.SerialNumber) {
			strarray[0] = hdr->ID.Manufacturer.SerialNumber;
			mxSetField(Manufacturer,0,"SerialNumber",mxCreateCharMatrixFromStrings(1,strarray));
		}
		mxSetField(HDR,0,"Manufacturer",Manufacturer);

	if (VERBOSE_LEVEL>8) 
		fprintf(stdout,"[148] going for SCLOSE\n");

		mxSetField(HDR,0,"Patient",Patient);

#ifndef mexSOPEN
		plhs[1] = HDR; 
	}
#else
	plhs[0] = HDR; 
#endif

	if (VERBOSE_LEVEL>7) fprintf(stdout,"[151] going for SCLOSE\n");
#ifdef CHOLMOD_H
	hdr->Calib = NULL; // is refering to &RR, do not destroy
#endif
	if (VERBOSE_LEVEL>7) fprintf(stdout,"[156] SCLOSE finished\n");
	destructHDR(hdr);
	hdr = NULL; 
	if (VERBOSE_LEVEL>7) fprintf(stdout,"[157] SCLOSE finished\n");
};