Ejemplo n.º 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;
}
Ejemplo n.º 2
0
/****************************************************************************
   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;
				}
			}