1 /*
2     sopen_axg_read is a helper function to sopen, reading AXG data.
3 
4     Copyright (C) 2008-2014 Alois Schloegl <alois.schloegl@gmail.com>
5     This file is part of the "BioSig for C/C++" repository
6     (biosig4c++) at http://biosig.sf.net/
7 
8     BioSig is free software; you can redistribute it and/or
9     modify it under the terms of the GNU General Public License
10     as published by the Free Software Foundation; either version 3
11     of the License, or (at your option) any later version.
12 
13     This program is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16     GNU General Public License for more details.
17 
18     You should have received a copy of the GNU General Public License
19     along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #define _GNU_SOURCE
23 
24 #include <assert.h>
25 #include <ctype.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/stat.h>
29 #include <iconv.h>
30 
31 #if !defined(__APPLE__) && defined (_LIBICONV_H)
32  #define iconv		libiconv
33  #define iconv_open	libiconv_open
34  #define iconv_close	libiconv_close
35 #endif
36 
37 
38 #include "../biosig-dev.h"
39 
40 #define min(a,b)        (((a) < (b)) ? (a) : (b))
41 #define max(a,b)        (((a) > (b)) ? (a) : (b))
42 
sopen_axg_read(HDRTYPE * hdr)43 void sopen_axg_read(HDRTYPE* hdr) {
44 
45 		hdr->FILE.LittleEndian = 0;
46 
47 		// read whole file into RAM
48 		size_t count = hdr->HeadLen;
49 		while (!ifeof(hdr)) {
50 			const int minsize = 1024;
51 			hdr->AS.Header = (uint8_t*)realloc(hdr->AS.Header, 2*count+minsize);
52 			count += ifread(hdr->AS.Header+count, 1, count+minsize, hdr);
53 		}
54 		ifclose(hdr);
55 
56 if (VERBOSE_LEVEL > 7) fprintf(stdout,"%s (line %i) %p %i\n", __FILE__, __LINE__, hdr->CHANNEL, hdr->NS );
57 
58 		int32_t nCol;
59 		switch ((int) hdr->VERSION) {
60 		case 1:
61 		case 2:
62 			nCol         = bei16p(hdr->AS.Header+6);
63 			hdr->HeadLen = 8;
64 			break;
65 		case 3:
66 		case 4:
67 		case 5:
68 		case 6:
69 			nCol      = bei32p(hdr->AS.Header+8);
70 			hdr->HeadLen = 12;
71 			break;
72 		default:
73 			biosigERROR(hdr,B4C_FORMAT_UNSUPPORTED,"AXG - unsupported version number");
74 			return;
75 		}
76 
77 		/* hack: for now each trace (i.e. column) becomes a separate channel -
78 			later the traces of the channels will be reorganized
79 		 */
80 		CHANNEL_TYPE *TEMPCHANNEL = (CHANNEL_TYPE*)malloc(nCol*sizeof(CHANNEL_TYPE));
81 		char **ValLabel = (char**)malloc(nCol*sizeof(char*));
82 		uint32_t *keyLabel = (uint32_t*)malloc(nCol*sizeof(uint32_t));
83 
84 if (VERBOSE_LEVEL > 7) fprintf(stdout,"%s (line %i) %p %i\n", __FILE__, __LINE__, hdr->CHANNEL, hdr->NS );
85 
86 		/******* read all column/traces ******/
87 		int32_t k;
88 		uint8_t *pos = hdr->AS.Header+hdr->HeadLen;
89 		hdr->SPR    = beu32p(pos);
90 		switch ((int) hdr->VERSION) {
91 		case 1:
92 			for (k = 0; k < nCol; k++) {
93 				CHANNEL_TYPE *hc = TEMPCHANNEL+k;
94 				hc->GDFTYP = 16; 	//float
95 				hc->PhysDimCode = 0;
96 				hc->SPR    = beu32p(pos);
97 
98 				int strlen = pos[4];   // string in Pascal format
99 				if (strlen > 79) {
100 					biosigERROR(hdr,B4C_FORMAT_UNSUPPORTED,"AXG - invalid title length ");
101 					return;
102 				}
103 
104 				/*  Organize channels  */
105 				uint32_t i;
106 				for (i = 0; i < hdr->NS; i++) {
107 					// check if channel with same title already exists
108 					if (!memcmp(ValLabel[hdr->NS], pos+4, strlen)) {
109 						keyLabel[k] = hdr->NS;
110 						break;
111 					}
112 				}
113 				if (i==hdr->NS) {
114 					// in case of new title, add another channel
115 					ValLabel[hdr->NS] = (char*)pos+4;
116 					keyLabel[k] = hdr->NS;
117 					hdr->NS++;
118 				}
119 
120 				// start of data section
121 				hc->bufptr = pos+84;
122 				pos += 84 + hc->SPR * sizeof(float);
123 
124 			}
125 			break;
126 		case 2:
127 			for (k = 0; k < nCol; k++) {
128 				CHANNEL_TYPE *hc = TEMPCHANNEL+k;
129 				hc->GDFTYP = 3;		// int16
130 				hc->PhysDimCode = 0;
131 				hc->SPR    = beu32p(pos);
132 				if (k==0) {
133 					hc->Off    = bef32p(pos+84);
134 					hc->Cal    = bef32p(pos+88);
135 					hc->bufptr = NULL;
136 					hdr->SampleRate = 1.0 / hc->Cal;
137 				}
138 				else {
139 					hc->Cal    = bef32p(pos+84);
140 					hc->Off    = 0.0;
141 					hc->bufptr = pos + 88;
142 				}
143 				int strlen = pos[4];   // string in Pascal format
144 				if (strlen > 79) {
145 					biosigERROR(hdr,B4C_FORMAT_UNSUPPORTED,"AXG - invalid title length ");
146 					return;
147 				}
148 
149 				biosigERROR(hdr,B4C_FORMAT_UNSUPPORTED,"AXG - version 2 not supported yet ");
150 				return;
151 
152 				// start of data sectioB
153 				pos += (k==0 ? 92 : 88 + hc->SPR * sizeof(int16_t) );
154 
155 			}
156 			break;
157 		case 6:
158 			for (k=0; k < nCol; k++) {
159 
160 if (VERBOSE_LEVEL > 7) fprintf(stdout,"%s (line %i) %p %i\n", __FILE__, __LINE__, hdr->CHANNEL, k );
161 
162 				CHANNEL_TYPE *hc  = TEMPCHANNEL+k;
163 				hc->SPR           = beu32p(pos);
164 				uint32_t datatype = beu32p(pos+4);
165 				size_t titleLen   = beu32p(pos+8);
166 				char *inbuf       = (char*)pos + 12;
167 				hc->bufptr        = pos + 12 + titleLen;
168 
169 if (VERBOSE_LEVEL > 7) fprintf(stdout,"%s (line %i) %i %i %i\n", __FILE__, __LINE__, (int)datatype, (int)titleLen, (int)hc->SPR);
170 				/*
171 				// The only types used for data file columns are...
172 				//   ShortArrayType = 4     IntArrayType = 5
173 				//   FloatArrayType = 6     DoubleArrayType = 7
174 				//   SeriesArrayType = 9    ScaledShortArrayType = 10
175 				*/
176 				hc->Cal = 1.0;
177 				hc->Off = 0.0;
178 				hc->GDFTYP = datatype;	//TEMPCHANNEL.GDFTYP uses a different encoding than standard GDFTYP
179 				hc->OnOff = 1;
180 				switch (datatype) {
181 				case 4: // int16
182 					hc->GDFTYP=3; break;
183 				case 5: // int32
184 					hc->GDFTYP=5; break;
185 				case 6: // float32
186 					hc->GDFTYP=16; break;
187 				case 7: // double
188 					hc->GDFTYP=17; break;
189 				case 9: hc->GDFTYP = 17;  // series
190 					// double firstval  = bef64p(hc->bufptr);
191 					double increment = bef64p(hc->bufptr+8);
192 					hc->bufptr = NULL;
193 					hc->OnOff = 0;
194 					if (!memcmp(inbuf,"\0T\0i\0m\0e\0 \0(\0s\0)\0",8)) {
195 						hdr->SampleRate = 1.0/increment;
196 						//hc->OnOff = 2;	// time axis
197 					}
198 					else {
199 						biosigERROR(hdr, B4C_FORMAT_UNSUPPORTED, "AXG: series data not being a Time axis is not supported. ");
200 						return;
201 					}
202 					break;
203 				case 10: // scaled short
204 					hc->Cal = bef64p(hc->bufptr);
205 					hc->Off = bef64p(hc->bufptr+8);
206 					break;
207 				default:
208 					hc->OnOff = 0;
209 				}
210 
211 if (VERBOSE_LEVEL > 7) fprintf(stdout,"%s (line %i) %p %i\n", __FILE__, __LINE__, hdr->CHANNEL, k );
212 				if (VERBOSE_LEVEL > 7) fprintf(stdout,"%s (line %i): %i %i %i %i\n", __FILE__, __LINE__, (int)hc->SPR, (int)datatype, (int)titleLen, (int)(pos-hdr->AS.Header) );
213 
214 				/*  Organize channels
215 					find number of channels and
216 					setup data structure that assignes each column to a channel
217 					ValLabel contains the different Labels - one for each channel
218 					keyLabel contains the channel number for the corresponding column
219 				*/
220 				uint32_t i;
221 				for (i = 0; i < hdr->NS; i++) {
222 					// check if channel with same title already exists
223 					uint32_t prevTitleLen = beu32p((uint8_t*)(ValLabel[i])-4);
224 					if ((titleLen == prevTitleLen) && !memcmp(ValLabel[i], pos+12, titleLen)) {
225 						keyLabel[k] = i;
226 						break;
227 					}
228 				}
229 if (VERBOSE_LEVEL > 7) fprintf(stdout,"%s (line %i) %p %i\n", __FILE__, __LINE__, hdr->CHANNEL, k );
230 				if (i==hdr->NS) {
231 					// in case of new title, add another channel
232 					ValLabel[hdr->NS] = (char*)pos+12; 	// pointer to title of channel 'nLabel', length of title is stored in beu32p(pos+8)
233 					keyLabel[k] = hdr->NS;
234 					hdr->NS++;
235 				}
236 
237 if (VERBOSE_LEVEL > 7) fprintf(stdout,"%s (line %i) %p %i\n", __FILE__, __LINE__, hdr->CHANNEL, k );
238 				// pointer to data sections
239 				hc->bufptr = pos + 12 + titleLen;
240 
241 
242 				// move pos to the starting position of the next column
243 				pos += 12 + titleLen;
244 				// position of next column
245 				switch (datatype) {
246 				case 4:
247 					pos += hc->SPR * sizeof(int16_t);
248 					break;
249 				case 5: //int32
250 				case 6: //float
251 					pos += hc->SPR * 4;
252 					break;
253 				case 7:
254 					pos += hc->SPR * sizeof(double);
255 					break;
256 				case 9:
257 					pos += 2 * sizeof(double);
258 					break;
259 				case 10:
260 					pos += 2 * sizeof(double) + hc->SPR * sizeof(int16_t);
261 					break;
262 				default:
263 					biosigERROR(hdr,B4C_FORMAT_UNSUPPORTED,"error reading AXG: unsupported data type");
264 				}
265 				if (VERBOSE_LEVEL > 7) fprintf(stdout,"%s (line %i): %i %i %i %i\n", __FILE__, __LINE__, (int)hc->SPR, (int)datatype, (int)titleLen, (int)(pos-hdr->AS.Header) );
266 
267 			}
268 			break;
269 		default:
270 			biosigERROR(hdr,B4C_FORMAT_UNSUPPORTED,"AXG version is not supported");
271 		}
272 
273 if (VERBOSE_LEVEL > 7) fprintf(stdout,"%s (line %i) %p %p %i %i\n", __FILE__, __LINE__, TEMPCHANNEL, hdr->CHANNEL, (int)hdr->NS , (int)sizeof(CHANNEL_TYPE));
274 
275 		/* convert columns/traces into channels */
276 		hdr->CHANNEL = (CHANNEL_TYPE*)realloc(hdr->CHANNEL, hdr->NS * sizeof(CHANNEL_TYPE));
277 		uint32_t ns;
278 		for (ns=0; ns < hdr->NS; ns++) {
279 			CHANNEL_TYPE *hc = hdr->CHANNEL + ns;
280 			hc->SPR = 0;
281 			hc->GDFTYP = 0;
282 			hc->OnOff = 1;
283 		}
284 		size_t EventN = 0;
285 		hdr->SPR = 0;
286 
287 if (VERBOSE_LEVEL > 7) fprintf(stdout,"%s (line %i) NS=%i nCol=%i\n", __FILE__, __LINE__, hdr->NS, nCol );
288 
289 		int flag_traces_of_first_sweep_done=0;
290 		for (k=0; k < nCol; k++) {
291 			/*
292 				copy essential parameters ´(GDFTYP, OnOff, Cal, Off) from TEMPCHANNEL
293 				and keep track of the number of samples SPR for each channel
294 			*/
295 
296 			// define GDFTYP, Cal, Off
297 			ns = keyLabel[k];	// channel number for current column
298 			CHANNEL_TYPE *hc = hdr->CHANNEL + ns;
299 
300 			hc->SPR   += TEMPCHANNEL[k].SPR;
301 
302 			switch (TEMPCHANNEL[k].GDFTYP) {
303 			case 4: // int16
304 				if (hc->GDFTYP < 3) hc->GDFTYP = 3;
305 				break;
306 			case 5: // int32
307 				if (hc->GDFTYP < 5) hc->GDFTYP = 5;
308 				break;
309 			case 6: // float32
310 				if (hc->GDFTYP < 16) hc->GDFTYP = 16;
311 				break;
312 			case 7: // double
313 				if (hc->GDFTYP < 17) hc->GDFTYP = 17;
314 				break;
315 			case 10: hc->GDFTYP = 3;  // scaled short
316 				if (hc->GDFTYP < 16) hc->GDFTYP = 16;
317 				break;
318 			default:
319 				hc->OnOff = 0;
320 			}
321 
322 			if (!flag_traces_of_first_sweep_done) {
323 				hc->Cal    = TEMPCHANNEL[k].Cal;
324 				hc->Off    = TEMPCHANNEL[k].Off;
325 			}
326 			else {
327 				if (hc->Cal != TEMPCHANNEL[k].Cal || hc->Off != TEMPCHANNEL[k].Off) {
328 					// in case input is scaled short, output shoud be float
329 					hc->GDFTYP = max(16, hc->GDFTYP);
330 				}
331 			}
332 
333 			if (hdr->SPR < hc->SPR) hdr->SPR = hc->SPR;
334 
335 			if (ns+1 == hdr->NS) {
336 				flag_traces_of_first_sweep_done = 1;
337 
338 				// ... add segment break in event table.
339 				if ( hdr->EVENT.N + 1 >= EventN ) {
340 					EventN += max(EventN, 16);
341 					hdr->EVENT.POS = (uint32_t*)realloc(hdr->EVENT.POS, EventN * sizeof(*hdr->EVENT.POS));
342 					hdr->EVENT.TYP = (uint16_t*)realloc(hdr->EVENT.TYP, EventN * sizeof(*hdr->EVENT.TYP));
343 				}
344 				hdr->EVENT.TYP[hdr->EVENT.N] = 0x7ffe;
345 				hdr->EVENT.POS[hdr->EVENT.N] = hdr->SPR;
346 				hdr->EVENT.N++;
347 			}
348 		}
349 		hdr->EVENT.N--;		// ignore last separator event
350 
351 		hdr->NRec = hdr->SPR;
352 		hdr->SPR = 1;
353 		uint32_t bi8 = 0;
354 		for (ns=0; ns < hdr->NS; ns++) {
355 			CHANNEL_TYPE *hc = hdr->CHANNEL+ns;
356 			hc->bi8 = bi8;
357 			hc->bi  = bi8/8;
358 			if (hc->OnOff != 1)
359 				hc->SPR = 0;
360 			else
361 				bi8 += GDFTYP_BITS[hc->GDFTYP];
362 		}
363 		hdr->AS.bpb = bi8/8;
364 
365 		for (ns=0; ns < hdr->NS; ns++) {
366 			CHANNEL_TYPE *hc = hdr->CHANNEL+ns;
367 
368 			// define hdr->channel[.].Label, hdr->channel[.].PhysDim
369 			if (hdr->Version <= 2) {
370 				// PascalToCString(ValLabel[ns]); 	// shift by 1 byte and terminate 0 char
371 				int strlen = min(ValLabel[ns][0],MAX_LENGTH_LABEL);
372 				strncpy(hc->Label, (ValLabel[ns])+1, strlen);
373 
374 				char *u1 = strrchr(ValLabel[ns],'(');
375 				char *u2 = strrchr(ValLabel[ns],')');
376 				if (u1 != NULL && u2 != NULL && u1 < u2) {
377 					*u1 = 0;
378 					*u2 = 0;
379 					hc->PhysDimCode = PhysDimCode(u1+1);
380 				}
381 			}
382 			else if (hdr->Version <= 6) {
383 				char *inbuf       = ValLabel[ns];
384 				size_t inlen      = beu32p((uint8_t*)(ValLabel[ns])-4);
385 				char *outbuf      = hc->Label;
386 				size_t outlen     = MAX_LENGTH_LABEL+1;
387 #if  defined(_ICONV_H) || defined (_LIBICONV_H)
388 				iconv_t ICONV = iconv_open("UTF-8","UCS-2BE");
389 				size_t reticonv = iconv(ICONV, &inbuf, &inlen, &outbuf, &outlen);
390 				iconv_close(ICONV);
391 
392 				if (VERBOSE_LEVEL > 7) fprintf(stdout,"%s (line %i): %i %i %i %"PRIiPTR"\n", __FILE__, __LINE__, (int)hc->SPR, (int)inlen, (int)(pos-hdr->AS.Header), reticonv );
393 
394 				if (reticonv == (size_t)(-1) ) {
395 					perror("AXG - conversion of title failed!!!");
396 					biosigERROR(hdr,B4C_FORMAT_UNSUPPORTED,"AXG - conversion of title failed");
397 					return;
398 				}
399 				*outbuf=0;
400 #else
401 				++inbuf;
402 				int i = min(MAX_LENGTH_LABEL, titleLen/2);
403 				for (; i>0 ; i-- ) {
404 					*outbuf= *inbuf;
405 					inbuf += 2;
406 					outbuf++;
407 				}
408 				outbuf = 0;
409 #endif
410 				char *u1 = strrchr(hc->Label,'(');
411 				char *u2 = strrchr(hc->Label,')');
412 				if (u1 != NULL && u2 != NULL && u1 < u2) {
413 					*u1 = 0;
414 					*u2 = 0;
415 					hc->PhysDimCode = PhysDimCode(u1+1);
416 				}
417 			}
418 
419 			// these might be reorganized below
420 			hc->DigMax  =  1e9;
421 			hc->DigMin  = -1e9;
422 			hc->PhysMax = hc->DigMax;
423 			hc->PhysMin = hc->DigMin;
424 
425 			hc->LeadIdCode = 0;
426 			hc->Transducer[0] = 0;
427 
428 			hc->Cal     =  1.0;
429 			hc->Off     =  0.0;
430 
431 			hc->TOffset   = 0;
432 			hc->HighPass  = NAN;
433 			hc->LowPass   = NAN;
434 			hc->Notch     = NAN;
435 			hc->Impedance = INFINITY;
436 			hc->fZ        = NAN;
437 			hc->XYZ[0] = 0.0;
438 			hc->XYZ[1] = 0.0;
439 			hc->XYZ[2] = 0.0;
440 		}
441 
442 		hdr->AS.rawdata = (uint8_t*)realloc( hdr->AS.rawdata, hdr->AS.bpb*hdr->SPR*hdr->NRec);
443 
444 		for (ns=0; ns < hdr->NS; ns++) {
445 			CHANNEL_TYPE *hc = hdr->CHANNEL + ns;
446 			hc->SPR = 0;
447 		}
448 		for (k=0; k < nCol; k++) {
449 
450 			ns = keyLabel[k];
451 			CHANNEL_TYPE *hc = hdr->CHANNEL + ns;
452 
453 			if (hc->OnOff != 1) continue;
454 
455 			uint32_t i;
456 			switch (hc->GDFTYP) {
457 			case 3:
458 				assert(TEMPCHANNEL[k].GDFTYP==4);
459 				for (i=0; i < TEMPCHANNEL[k].SPR; i++) {
460 					*(int16_t*)(hdr->AS.rawdata + hc->bi + (hc->SPR + i) * hdr->AS.bpb) = bei16p(TEMPCHANNEL[k].bufptr + i*2);
461 				}
462 				break;
463 			case 5:
464 				switch (TEMPCHANNEL[k].GDFTYP) {
465 				case 4:
466 					for (i=0; i < TEMPCHANNEL[k].SPR; i++) {
467 						*(int32_t*)(hdr->AS.rawdata + hc->bi + (hc->SPR + i) * hdr->AS.bpb) = bei16p(TEMPCHANNEL[k].bufptr + i*2);
468 					}
469 					break;
470 				case 5:
471 					for (i=0; i < TEMPCHANNEL[k].SPR; i++) {
472 						*(int32_t*)(hdr->AS.rawdata + hc->bi + (hc->SPR + i) * hdr->AS.bpb) = bei32p(TEMPCHANNEL[k].bufptr + i*4);
473 					}
474 					break;
475 				default:
476 					biosigERROR(hdr,B4C_FORMAT_UNSUPPORTED,"AXG - data conversion not supported ");
477 					return;
478 				}
479 				break;
480 			case 16:
481 				switch (TEMPCHANNEL[k].GDFTYP) {
482 				case 4:
483 					for (i=0; i < TEMPCHANNEL[k].SPR; i++) {
484 						*(float*)(hdr->AS.rawdata + hc->bi + (hc->SPR + i) * hdr->AS.bpb) = (float)bei16p(TEMPCHANNEL[k].bufptr + i*2);
485 					}
486 					break;
487 				case 5:
488 					for (i=0; i < TEMPCHANNEL[k].SPR; i++) {
489 						*(float*)(hdr->AS.rawdata + hc->bi + (hc->SPR + i) * hdr->AS.bpb) = (float)bei32p(TEMPCHANNEL[k].bufptr + i*4);
490 					}
491 					break;
492 				case 6:
493 					for (i=0; i < TEMPCHANNEL[k].SPR; i++) {
494 						*(float*)(hdr->AS.rawdata + hc->bi + (hc->SPR + i) * hdr->AS.bpb) = bef32p(TEMPCHANNEL[k].bufptr + i*4);
495 					}
496 					break;
497 				case 10:
498 					for (i=0; i < TEMPCHANNEL[k].SPR; i++) {
499 						*(float*)(hdr->AS.rawdata + hc->bi + (hc->SPR + i) * hdr->AS.bpb) = bei16p(TEMPCHANNEL[k].bufptr + i*2) * TEMPCHANNEL[k].Cal + TEMPCHANNEL[k].Off;
500 					}
501 					break;
502 				default:
503 					biosigERROR(hdr,B4C_FORMAT_UNSUPPORTED,"AXG - data conversion not supported ");
504 					return;
505 				}
506 				break;
507 			case 17:
508 				switch (TEMPCHANNEL[k].GDFTYP) {
509 				case 4:
510 					for (i=0; i < TEMPCHANNEL[k].SPR; i++) {
511 						*(double*)(hdr->AS.rawdata + hc->bi + (hc->SPR + i) * hdr->AS.bpb) = (double)bei16p(TEMPCHANNEL[k].bufptr + i*2);
512 					}
513 					break;
514 				case 5:
515 					for (i=0; i < TEMPCHANNEL[k].SPR; i++) {
516 						*(double*)(hdr->AS.rawdata + hc->bi + (hc->SPR + i) * hdr->AS.bpb) = (double)bei32p(TEMPCHANNEL[k].bufptr + i*4);
517 					}
518 					break;
519 				case 6:
520 					for (i=0; i < TEMPCHANNEL[k].SPR; i++) {
521 						*(double*)(hdr->AS.rawdata + hc->bi + (hc->SPR + i) * hdr->AS.bpb) = (double)bef32p(TEMPCHANNEL[k].bufptr + i*4);
522 					}
523 					break;
524 				case 7:
525 					for (i=0; i < TEMPCHANNEL[k].SPR; i++) {
526 						*(double*)(hdr->AS.rawdata + hc->bi + (hc->SPR + i) * hdr->AS.bpb) = bef64p(TEMPCHANNEL[k].bufptr + i*8);
527 					}
528 					break;
529 				case 10:
530 					for (i=0; i < TEMPCHANNEL[k].SPR; i++) {
531 						*(double*)(hdr->AS.rawdata + hc->bi + (hc->SPR + i) * hdr->AS.bpb) = bei16p(TEMPCHANNEL[k].bufptr + i*2) * TEMPCHANNEL[k].Cal + TEMPCHANNEL[k].Off;
532 					}
533 					break;
534 				default:
535 					biosigERROR(hdr,B4C_FORMAT_UNSUPPORTED,"AXG - data conversion not supported ");
536 					return;
537 				}
538 				break;
539 			default:
540 				biosigERROR(hdr,B4C_FORMAT_UNSUPPORTED,"AXG - unsupported target data type");
541 				return;
542 			}
543 			hc->SPR += TEMPCHANNEL[k].SPR;
544 		}
545 
546 		for (ns=0; ns < hdr->NS; ns++) {
547 			CHANNEL_TYPE *hc = hdr->CHANNEL + ns;
548 			hc->SPR = hdr->SPR;
549 		}
550 		// free intermediate data structure to reorganized column/trace to channels
551 		if(TEMPCHANNEL) free(TEMPCHANNEL);
552 		if(keyLabel) free(keyLabel);
553 		if(ValLabel) free(ValLabel);
554 
555 		// data is stored on hdr->AS.rawdata in such a way that swapping must not be applied
556 		hdr->FILE.LittleEndian = (__BYTE_ORDER == __LITTLE_ENDIAN);
557 		hdr->AS.first  = 0;
558 		hdr->AS.length = (size_t)hdr->NRec;
559 
560 		// read Comments
561 		size_t szComments = beu32p(pos);
562 		char  *inbuf       = (char*)pos+4;
563 		char  *Comments    = malloc(szComments+1);
564 		char  *outbuf      = Comments;
565 		size_t outlen     = szComments+1;
566 		size_t inlen      = szComments;
567 
568 		iconv_t ICONV = iconv_open("UTF-8","UCS-2BE");
569 		size_t reticonv = iconv(ICONV, &inbuf, &inlen, &outbuf, &outlen);
570 		iconv_close(ICONV);
571 		if (reticonv == (size_t)(-1) ) {
572 			perror("AXG - conversion of comments failed!!!");
573 			biosigERROR(hdr,B4C_FORMAT_UNSUPPORTED,"AXG - conversion of comments failed");
574 			return;
575 		}
576 		Comments[outlen]=0;
577 
578 		if (VERBOSE_LEVEL >7)
579 			fprintf(stdout,"\n=== COMMENT === \n %s\n",Comments);
580 		pos += 4+szComments;
581 
582 
583 		// read Notes
584 		size_t szNotes  = beu32p(pos);
585 		inbuf           = (char*)pos+4;
586 		char *Notes     = malloc(szNotes+1);
587 		outbuf = Notes;
588 		outlen = szNotes+1;
589 		inlen  = szNotes;
590 
591 		ICONV    = iconv_open("UTF-8","UCS-2BE");
592 		reticonv = iconv(ICONV, &inbuf, &inlen, &outbuf, &outlen);
593 		iconv_close(ICONV);
594 		if ( reticonv == (size_t)(-1) ) {
595 			biosigERROR(hdr,B4C_FORMAT_UNSUPPORTED,"AXG - conversion of Notes failed");
596 			return;
597 		}
598 		Notes[outlen]=0;
599 
600 		if (VERBOSE_LEVEL >7)
601 			fprintf(stdout,"=== NOTES === \n %s\n",Notes);
602 		pos += 4+szNotes;
603 
604 		/******  parse Date and Time ********/
605 		struct tm T;
606 #ifdef __GLIBC__
607 		strptime(strstr(Notes,"Created on ")+11, "%a %b %d %Y", &T);
608 		strptime(strstr(Notes,"acquisition at ")+15, "%T", &T);
609 		hdr->T0 = tm_time2gdf_time(&T);
610 #else
611 		char DATE[30];
612 		strncpy(DATE, strstr(Notes,"Created on ")+11, 30);
613 		DATE[29] = 0;
614 		strtok(DATE, "\n\r");	// cut at newline
615 		char *tmp = strtok(DATE, " ");	// day of week - skip
616 
617 		tmp = strtok(NULL, " ");	// abreviated month name
618 		T.tm_mon = month_string2int(tmp);
619 
620 		tmp = strtok(NULL, " ");	// day of month
621 		T.tm_mday = atoi(tmp);
622 
623 		tmp = strtok(NULL
624 
625 , " ");	// year
626 		T.tm_year = atoi(tmp) - 1900;
627 
628 		strncpy(DATE, strstr(Notes,"acquisition at ")+15, 9);
629 		DATE[9] = 0;
630 		tmp = strtok(DATE, " :");
631 		T.tm_hour = atoi(tmp);
632 		tmp = strtok(NULL, " :");
633 		T.tm_min  = atoi(tmp);
634 		tmp = strtok(NULL, " :");
635 		T.tm_sec  = atoi(tmp);
636 
637 		hdr->T0 = tm_time2gdf_time(&T);
638 
639 #endif
640 
641 		hdr->AS.fpulse = Notes;
642 		free(Comments);
643 
644 if (VERBOSE_LEVEL > 7) fprintf(stdout,"%s (line %i)\n", __FILE__, __LINE__ );
645 
646 }
647 
648 
649 
650