1 /*
2 
3     Copyright (C) 2005-2018 Alois Schloegl <alois.schloegl@ist.ac.at>
4 
5     This file is part of the "BioSig for C/C++" repository
6     (biosig4c++) at http://biosig.sf.net/
7 
8 
9     This program is free software; you can redistribute it and/or
10     modify it under the terms of the GNU General Public License
11     as published by the Free Software Foundation; either version 3
12     of the License, or (at your option) any later version.
13 
14 
15  */
16 
17 
18 #include <assert.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include "../biosig-dev.h"
23 
24 #define min(a,b)        (((a) < (b)) ? (a) : (b))
25 #define max(a,b)        (((a) > (b)) ? (a) : (b))
26 
27 #ifdef __cplusplus
28 extern "C" {
29 #endif
30 
sopen_SCP_write(HDRTYPE * hdr)31 int sopen_SCP_write(HDRTYPE* hdr) {
32 /*
33 	This function is an auxillary function and is only called by the function SOPEN in "biosig.c"
34 
35 	Input:
36 		HDRTYPE *hdr	// defines the HDR structure according to "biosig.h"
37 		hdr->VERSION 	specifies the target version
38 */
39 	uint8_t*	ptr; 	// pointer to memory mapping of the file layout
40 	uint8_t*	PtrCurSect;	// point to current section
41 	int		curSect;
42 	uint32_t 	len;
43 	uint16_t 	crc;
44 	uint32_t	i;
45 	uint32_t 	sectionStart;
46 	struct tm* 	T0_tm;
47 	double 		AVM, avm;
48 	uint16_t	avm16;
49 	struct aecg*	aECG;
50 
51 	assert(hdr != NULL);
52 	assert(hdr->TYPE == SCP_ECG);
53 
54 	if (VERBOSE_LEVEL>7) fprintf(stdout,"%s (line %i) : V%f\n",__FILE__,__LINE__,hdr->VERSION);
55 
56 	if ((fabs(hdr->VERSION - 1.3)<0.01) && (fabs(hdr->VERSION-2.0)<0.01) && (fabs(hdr->VERSION-3.0)<0.01))
57 		fprintf(stderr,"Warning SOPEN (SCP-WRITE): Version %f not supported\n",hdr->VERSION);
58 
59 	uint8_t versionSection  = (hdr->VERSION < 3.0) ? 20 : 29; // (uint8_t)round(hdr->VERSION*10); // implemented version number
60 	uint8_t versionProtocol = versionSection;
61 
62 	if (hdr->aECG==NULL) {
63 		fprintf(stderr,"Warning SOPEN_SCP_WRITE: No aECG info defined\n");
64 		hdr->aECG = malloc(sizeof(struct aecg));
65 		aECG = (struct aecg*)hdr->aECG;
66 		aECG->diastolicBloodPressure=0.0;
67 		aECG->systolicBloodPressure=0.0;
68 		aECG->MedicationDrugs="/0";
69 		aECG->ReferringPhysician="/0";
70 		aECG->LatestConfirmingPhysician="/0";
71 		aECG->Diagnosis="/0";
72 		aECG->EmergencyLevel=0;
73 #if (BIOSIG_VERSION < 10500)
74 		aECG->Section8.NumberOfStatements = 0;
75 		aECG->Section8.Statements = NULL;
76 		aECG->Section11.NumberOfStatements = 0;
77 		aECG->Section11.Statements = NULL;
78 #endif
79 	}
80 	else
81 		aECG = (struct aecg*)hdr->aECG;
82 
83 
84 //fprintf(stdout,"SCP-Write: IIb %s\n",hdr->aECG->ReferringPhysician);
85 	/* predefined values */
86 	aECG->Section1.Tag14.INST_NUMBER 	= 0;		// tag 14, byte 1-2
87 	aECG->Section1.Tag14.DEPT_NUMBER 	= 0;		// tag 14, byte 3-4
88 	aECG->Section1.Tag14.DEVICE_ID 	= 0;		// tag 14, byte 5-6
89 	aECG->Section1.Tag14.DeviceType 	= 0;		// tag 14, byte 7: 0: Cart, 1: System (or Host)
90 	aECG->Section1.Tag14.MANUF_CODE 	= 255;		// tag 14, byte 8 (MANUF_CODE has to be 255)
91 	aECG->Section1.Tag14.MOD_DESC  	= "Cart1";	// tag 14, byte 9 (MOD_DESC has to be "Cart1")
92 	aECG->Section1.Tag14.VERSION	= versionSection;	// tag 14, byte 15 (VERSION * 10)
93 	aECG->Section1.Tag14.PROT_COMP_LEVEL = 0xA0;	// tag 14, byte 16 (PROT_COMP_LEVEL has to be 0xA0 => level II)
94 		// tag 14, byte 17 (LANG_SUPP_CODE has to be 0x00 => Ascii only, latin and 1-byte code, 0x37: UTF-8)
95 	aECG->Section1.Tag14.LANG_SUPP_CODE  = (versionSection < 25) ? 0x00 : 0x37;
96 	aECG->Section1.Tag14.ECG_CAP_DEV 	= 0xD0;		// tag 14, byte 18 (ECG_CAP_DEV has to be 0xD0 => Acquire, (No Analysis), Print and Store)
97 	aECG->Section1.Tag14.MAINS_FREQ  	= 0;		// tag 14, byte 19 (MAINS_FREQ has to be 0: unspecified, 1: 50 Hz, 2: 60Hz)
98 	aECG->Section1.Tag14.ANAL_PROG_REV_NUM 	= "";
99 	aECG->Section1.Tag14.SERIAL_NUMBER_ACQ_DEV = "";
100 	aECG->Section1.Tag14.ACQ_DEV_SYS_SW_ID 	= "";
101 	aECG->Section1.Tag14.ACQ_DEV_SCP_SW	= "OpenECG XML-SCP 1.00"; // tag 14, byte 38 (SCP_IMPL_SW has to be "OpenECG XML-SCP 1.00")
102 	aECG->Section1.Tag14.ACQ_DEV_MANUF 	= "Manufacturer";	// tag 14, byte 38 (ACQ_DEV_MANUF has to be "Manufacturer")
103 
104 	aECG->Section5.Length = 0;
105 	aECG->Section6.Length = 0;
106 
107 
108 	/*  */
109 	aECG->FLAG.HUFFMAN = 0;
110 	aECG->FLAG.REF_BEAT= 0;
111 	aECG->FLAG.DIFF    = 0;
112 	aECG->FLAG.BIMODAL = 0;
113 
114 
115 	/*
116 		check channels:
117 		disable channels that do not have a known ECG LeadId
118 		disable channels with physical units other than Voltage.
119 		The number of channels for conversion is stored in NS.
120 	*/
121 	typeof(hdr->NS) NS = 0, k;
122 	for (k=0; k < hdr->NS; k++) {
123 		CHANNEL_TYPE *CH=hdr->CHANNEL+k;
124 
125 		if ( CH->LeadIdCode > 255) CH->OnOff = 0;
126 		if ( (CH->PhysDimCode & 0xffe0) != PhysDimCode("V")) CH->OnOff = 0;
127 
128 		if (CH->OnOff != 1) continue;
129 		NS++;
130 	}
131 
132 	if (VERBOSE_LEVEL>7) fprintf(stdout,"%s (line %i) : v%f VERSION=%i\n",__FILE__,__LINE__, hdr->VERSION, versionSection);
133 
134 	ptr = (uint8_t*)hdr->AS.Header;
135 
136 	int NSections = (versionSection < 25) ? 12 : 19;
137 	// initialize section 0
138 	sectionStart  = 6+16+NSections*10;
139 	ptr = (uint8_t*)realloc(ptr,sectionStart);
140 	memset(ptr,0,sectionStart);
141 
142 	uint32_t curSectLen; // current section length
143 	for (curSect=NSections-1; curSect>=0; curSect--) {
144 
145 		curSectLen = 0; // current section length
146 		//ptr = (uint8_t*)realloc(ptr,sectionStart+curSectLen);
147 
148 //		if (VERBOSE_LEVEL>7) fprintf(stdout,"%s (line %i) : Section %i/%i %i %p\n",__FILE__,__LINE__,curSect,NSections,sectionStart,ptr);
149 
150 		if (curSect==0)  // SECTION 0
151 		{
152 			hdr->HeadLen = sectionStart; // length of all other blocks together
153 			ptr = (uint8_t*)realloc(ptr,hdr->HeadLen); // total file length
154 
155 			curSectLen  = 16; // current section length
156 			sectionStart = 6;
157 
158 			curSectLen += NSections*10;
159 		}
160 		else if (curSect==1)  // SECTION 1
161 		{
162 			ptr = (uint8_t*)realloc(ptr,sectionStart+10000);
163 			PtrCurSect = ptr+sectionStart;
164 			curSectLen = 16; // current section length
165 			char *nextPartOfPatientName=hdr->Patient.Name;
166 
167 			if (VERBOSE_LEVEL>7) fprintf(stdout,"Section 1 Tag 0 \n");
168 
169 			// Tag 0 (max len = 64)
170 			if (!hdr->FLAG.ANONYMOUS && (nextPartOfPatientName != NULL) && strlen(nextPartOfPatientName))
171 			{
172 				*(ptr+sectionStart+curSectLen) = 0;	// tag
173 				len = strcspn(nextPartOfPatientName, "\x1f");
174 				leu16a(len, ptr+sectionStart+curSectLen+1);	// length
175 				strncpy((char*)ptr+sectionStart+curSectLen+3,nextPartOfPatientName,len);	// field
176 				nextPartOfPatientName += len+1;
177 				curSectLen += len+3;
178 			}
179 
180 			if (VERBOSE_LEVEL>7) fprintf(stdout,"Section 1 Tag 1 \n");
181 
182 			// Tag 1 (max len = 64) Firstname
183 			if (!hdr->FLAG.ANONYMOUS && (nextPartOfPatientName != NULL) && strlen(nextPartOfPatientName))
184 			{
185 				*(ptr+sectionStart+curSectLen) = 1;	// tag
186 				len = strcspn(nextPartOfPatientName, "\x1f");
187 				leu16a(len, ptr+sectionStart+curSectLen+1);	// length
188 				strncpy((char*)ptr+sectionStart+curSectLen+3,nextPartOfPatientName,len);	// field
189 				nextPartOfPatientName += len+1;
190 				curSectLen += len+3;
191 			}
192 
193 			// Tag 2 (max len = 64) Patient ID
194 			if (VERBOSE_LEVEL>7) fprintf(stdout,"Section 1 Tag 2 \n");
195 
196 //			if (hdr->Patient.Id != NULL) {
197 			if (strlen(hdr->Patient.Id)>0) {
198 				*(ptr+sectionStart+curSectLen) = 2;	// tag
199 				len = strlen(hdr->Patient.Id) + 1;
200 				leu16a(len, ptr+sectionStart+curSectLen+1);	// length
201 				strncpy((char*)ptr+sectionStart+curSectLen+3,hdr->Patient.Id,len);	// field
202 				curSectLen += len+3;
203 			}
204 
205 			// Tag 3 (max len = 64) Second Last Name
206 			if (VERBOSE_LEVEL>7) fprintf(stdout,"Section 1 Tag 3 \n");
207 
208 			if (!hdr->FLAG.ANONYMOUS && (nextPartOfPatientName != NULL) && strlen(nextPartOfPatientName))
209 			{
210 				*(ptr+sectionStart+curSectLen) = 3;	// tag
211 				len = strcspn(nextPartOfPatientName, "\x1f");
212 				leu16a(len, ptr+sectionStart+curSectLen+1);	// length
213 				strncpy((char*)ptr+sectionStart+curSectLen+3,nextPartOfPatientName,len);	// field
214 				nextPartOfPatientName += len+1;
215 				curSectLen += len+3;
216 			}
217 
218 			// Tag 5 (len = 4)
219 			if ((hdr->Patient.Birthday) > 0) {
220 				T0_tm = gdf_time2tm_time(hdr->Patient.Birthday);
221 
222 				*(ptr+sectionStart+curSectLen) = 5;	// tag
223 				leu16a(4, ptr+sectionStart+curSectLen+1);	// length
224 				leu16a(T0_tm->tm_year+1900, ptr+sectionStart+curSectLen+3);// year
225 				*(ptr+sectionStart+curSectLen+5) = (uint8_t)(T0_tm->tm_mon + 1);	// month
226 				*(ptr+sectionStart+curSectLen+6) = (uint8_t)(T0_tm->tm_mday); 	// day
227 				curSectLen += 7;
228 			}
229 
230 			// Tag 6 (len = 3)   Height
231 			if (hdr->Patient.Height>0.0) {
232 				*(ptr+sectionStart+curSectLen) = 6;	// tag
233 				leu16a(3, ptr+sectionStart+curSectLen+1);	// length
234 				leu16a(hdr->Patient.Height, ptr+sectionStart+curSectLen+3);	// value
235 				*(ptr+sectionStart+curSectLen+5) = 1;	// cm
236 				curSectLen += 6;
237 			}
238 
239 			// Tag 7 (len = 3)	Weight
240 			if (hdr->Patient.Weight>0.0) {
241 				*(ptr+sectionStart+curSectLen) = 7;	// tag
242 				leu16a(3, ptr+sectionStart+curSectLen+1);	// length
243 				leu16a(hdr->Patient.Weight, ptr+sectionStart+curSectLen+3);	// value
244 				*(ptr+sectionStart+curSectLen+5) = 1;	// kg
245 				curSectLen += 6;
246 			}
247 
248 			// Tag 8 (len = 1)
249 			if (hdr->Patient.Sex != 0) {
250 				*(ptr+sectionStart+curSectLen) = 8;	// tag
251 				leu16a(1, ptr+sectionStart+curSectLen+1);	// length
252 				*(ptr+sectionStart+curSectLen+3) = hdr->Patient.Sex;	// value
253 				curSectLen += 4;
254 			}
255 
256 			// Tag 11 (len = 2)
257 			if (aECG->systolicBloodPressure>0.0) {
258 				*(ptr+sectionStart+curSectLen) = 11;	// tag
259 				leu16a(2, ptr+sectionStart+curSectLen+1);	// length
260 				leu16a((uint16_t)aECG->systolicBloodPressure, ptr+sectionStart+curSectLen+3);	// value
261 				curSectLen += 5;
262 			};
263 
264 			// Tag 12 (len = 2)
265 			if (aECG->diastolicBloodPressure>0.0) {
266 				*(ptr+sectionStart+curSectLen) = 12;	// tag
267 				leu16a(2, ptr+sectionStart+curSectLen+1);	// length
268 				leu16a((uint16_t)aECG->diastolicBloodPressure, ptr+sectionStart+curSectLen+3);	// value
269 				curSectLen += 5;
270 			};
271 			// Tag 13 (max len = 80)
272 			aECG->Diagnosis="";
273 			len = strlen(aECG->Diagnosis);
274 			if (len>0) {
275 				*(ptr+sectionStart+curSectLen) = 13;	// tag
276 				len = min(64,len+1);
277 				leu16a(len, ptr+sectionStart+curSectLen+1);	// length
278 				strncpy((char*)(ptr+sectionStart+curSectLen+3),aECG->Diagnosis,len);
279 				curSectLen += 3+len;
280 			};
281 
282 			// Tag 14 (max len = 2 + 2 + 2 + 1 + 1 + 6 + 1 + 1 + 1 + 1 + 1 + 16 + 1 + 25 + 25 + 25 + 25 + 25)
283 			if (VERBOSE_LEVEL>7) fprintf(stdout,"Section 1 Tag 14 \n");
284 
285 			// Total = 161 (max value)
286 			*(ptr+sectionStart+curSectLen) = 14;	// tag
287 			//len = 41; 	// minimum length
288 			// leu16a(len, ptr+sectionStart+curSectLen+1);	// length
289 			memset(ptr+sectionStart+curSectLen+3,0,41);  // dummy value
290 
291 			curSectLen += 3;
292 			leu16a(aECG->Section1.Tag14.INST_NUMBER, ptr+sectionStart+curSectLen);
293 			leu16a(aECG->Section1.Tag14.DEPT_NUMBER, ptr+sectionStart+curSectLen+2);
294 			leu16a(aECG->Section1.Tag14.DEVICE_ID,   ptr+sectionStart+curSectLen+4);
295 			*(ptr+sectionStart+curSectLen+ 6) = aECG->Section1.Tag14.DeviceType;
296 			*(ptr+sectionStart+curSectLen+ 7) = aECG->Section1.Tag14.MANUF_CODE;	// tag 14, byte 7 (MANUF_CODE has to be 255)
297 			strncpy((char*)(ptr+sectionStart+curSectLen+8), aECG->Section1.Tag14.MOD_DESC, 6);	// tag 14, byte 7 (MOD_DESC has to be "Cart1")
298 			*(ptr+sectionStart+curSectLen+14) = versionSection;			// tag 14, byte 14 (VERSION has to be 20)
299 			*(ptr+sectionStart+curSectLen+14) = aECG->Section1.Tag14.VERSION;
300 			*(ptr+sectionStart+curSectLen+15) = aECG->Section1.Tag14.PROT_COMP_LEVEL; 	// tag 14, byte 15 (PROT_COMP_LEVEL has to be 0xA0 => level II)
301 			*(ptr+sectionStart+curSectLen+16) = aECG->Section1.Tag14.LANG_SUPP_CODE;	// tag 14, byte 16 (LANG_SUPP_CODE has to be 0x00 => Ascii only, latin and 1-byte code)
302 			*(ptr+sectionStart+curSectLen+17) = aECG->Section1.Tag14.ECG_CAP_DEV;	// tag 14, byte 17 (ECG_CAP_DEV has to be 0xD0 => Acquire, (No Analysis), Print and Store)
303 			*(ptr+sectionStart+curSectLen+18) = aECG->Section1.Tag14.MAINS_FREQ;	// tag 14, byte 18 (MAINS_FREQ has to be 0: unspecified, 1: 50 Hz, 2: 60Hz)
304 			*(ptr+sectionStart+curSectLen+35) = strlen(aECG->Section1.Tag14.ANAL_PROG_REV_NUM)+1;		// tag 14, byte 34 => length of ANAL_PROG_REV_NUM + 1 = 1
305 			uint16_t len1 = 36;
306 
307 			char* tmp;
308 			tmp = aECG->Section1.Tag14.ANAL_PROG_REV_NUM;
309 			len = min(25, strlen(tmp) + 1);
310 			strncpy((char*)(ptr+sectionStart+curSectLen+len1), tmp, len);
311 			len1 += len;
312 
313 			tmp = aECG->Section1.Tag14.SERIAL_NUMBER_ACQ_DEV;
314 			len = min(25, strlen(tmp) + 1);
315 			strncpy((char*)(ptr+sectionStart+curSectLen+len1), tmp, len);
316 			len1 += len;
317 
318 			tmp = aECG->Section1.Tag14.ACQ_DEV_SYS_SW_ID;
319 			len = min(25, strlen(tmp) + 1);
320 			strncpy((char*)(ptr+sectionStart+curSectLen+len1), tmp, len);
321 			len1 += len;
322 
323 			tmp = aECG->Section1.Tag14.ACQ_DEV_SCP_SW;
324 			len = min(25, strlen(tmp) + 1);
325 			strncpy((char*)(ptr+sectionStart+curSectLen+len1), tmp, len);
326 			len1 += len;
327 
328 			tmp = aECG->Section1.Tag14.ACQ_DEV_MANUF;
329 			len = min(25, strlen(tmp) + 1);
330 			strncpy((char*)(ptr+sectionStart+curSectLen+len1), tmp, len);
331 			len1 += len;
332 
333 			leu16a(len1, ptr+sectionStart+curSectLen+1-3);	// length
334 			curSectLen += len1;
335 
336 			// Tag 16 (max len = 80)
337 			if (VERBOSE_LEVEL>7) fprintf(stdout,"Section 1 Tag 16 \n");
338 			len = hdr->ID.Hospital ? strlen(hdr->ID.Hospital) : 0;
339 			if (len > 0) {
340 				*(ptr+sectionStart+curSectLen) = 16;	// tag
341 				len = min(64,len+1);
342 				leu16a(len, ptr+sectionStart+curSectLen+1);	// length
343 				strncpy((char*)(ptr+sectionStart+curSectLen+3),hdr->ID.Hospital,len);
344 				curSectLen += 3+len;
345 			}
346 
347 			// Tag 20 (max len = 64 )
348 			if (VERBOSE_LEVEL>7) fprintf(stdout,"Section 1 Tag 20 \n");
349 			len = aECG->ReferringPhysician ? strlen(aECG->ReferringPhysician) : 0;
350 			if (len > 0) {
351 				*(ptr+sectionStart+curSectLen) = 20;	// tag
352 				len = min(64,len+1);
353 				leu16a(len, ptr+sectionStart+curSectLen+1);	// length
354 				strncpy((char*)(ptr+sectionStart+curSectLen+3),aECG->ReferringPhysician,len);
355 				curSectLen += 3+len;
356 			};
357 
358 			// Tag 21 (max len = 64 )
359 			if (VERBOSE_LEVEL>7) fprintf(stdout,"Section 1 Tag 21 \n");
360 			len = aECG->MedicationDrugs ? strlen(aECG->MedicationDrugs) : 0;
361 			if (len>0) {
362 				*(ptr+sectionStart+curSectLen) = 21;	// tag
363 				len = min(64,len+1);
364 				leu16a(len, ptr+sectionStart+curSectLen+1);	// length
365 				strncpy((char*)(ptr+sectionStart+curSectLen+3),aECG->MedicationDrugs,len);
366 				curSectLen += 3+len;
367 			};
368 
369 			// Tag 22 (max len = 40 )
370 			if (VERBOSE_LEVEL>7) fprintf(stdout,"Section 1 Tag 22 \n");
371 			len = hdr->ID.Technician ? strlen(hdr->ID.Technician) : 0;
372 			if (len > 0) {
373 				*(ptr+sectionStart+curSectLen) = 22;	// tag
374 				len = min(64,len+1);
375 				leu16a(len, ptr+sectionStart+curSectLen+1);	// length
376 				strncpy((char*)(ptr+sectionStart+curSectLen+3),hdr->ID.Technician,len);
377 				curSectLen += 3+len;
378 			}
379 
380 			// Tag 24 ( len = 1 )
381 			if (VERBOSE_LEVEL>7) fprintf(stdout,"Section 1 Tag 24 \n");
382 			*(ptr+sectionStart+curSectLen) = 24;	// tag
383 			leu16a(1, ptr+sectionStart+curSectLen+1);	// length
384 			*(ptr+sectionStart+curSectLen+3) = aECG->EmergencyLevel;
385 			curSectLen += 4;
386 
387 			// Tag 25 (len = 4)
388 			if (VERBOSE_LEVEL>7) fprintf(stdout,"Section 1 Tag 25 \n");
389 
390 			gdf_time T1 = hdr->T0;
391 			T0_tm = gdf_time2tm_time(hdr->T0);
392 			T0_tm->tm_gmtoff = hdr->tzmin*60;
393 
394 			*(ptr+sectionStart+curSectLen) = 25;	// tag
395 			leu16a(4, ptr+sectionStart+curSectLen+1);	// length
396 			leu16a(T0_tm->tm_year+1900, ptr+sectionStart+curSectLen+3);// year
397 			*(ptr+sectionStart+curSectLen+5) = (uint8_t)(T0_tm->tm_mon + 1);// month
398 			*(ptr+sectionStart+curSectLen+6) = (uint8_t)T0_tm->tm_mday; 	// day
399 			curSectLen += 7;
400 
401 			// Tag 26 (len = 3)
402 			*(ptr+sectionStart+curSectLen) = 26;	// tag
403 			leu16a(3, ptr+sectionStart+curSectLen+1);	// length
404 			*(ptr+sectionStart+curSectLen+3) = (uint8_t)T0_tm->tm_hour;	// hour
405 			*(ptr+sectionStart+curSectLen+4) = (uint8_t)T0_tm->tm_min;	// minute
406 			*(ptr+sectionStart+curSectLen+5) = (uint8_t)T0_tm->tm_sec; 	// second
407 			curSectLen += 6;
408 
409 			if (NS>0)  {
410 				CHANNEL_TYPE *CH = hdr->CHANNEL;
411 				while (CH->OnOff != 1) CH++;
412 
413 			// Tag 27 (len = 3) highpass filter
414 			*(ptr+sectionStart+curSectLen) = 27;	// tag
415 			leu16a(2, ptr+sectionStart+curSectLen+1);	// length
416 			leu16a((uint16_t)CH->HighPass, ptr+sectionStart+curSectLen+3);	// hour
417 			curSectLen += 5;
418 
419 			// Tag 28 (len = 3)  lowpass filter
420 			*(ptr+sectionStart+curSectLen) = 28;	// tag
421 			leu16a(2, ptr+sectionStart+curSectLen+1);	// length
422 			leu16a((uint16_t)CH->LowPass, ptr+sectionStart+curSectLen+3);	// hour
423 			curSectLen += 5;
424 
425 			// Tag 29 (len = 1) filter bitmap
426 			uint8_t bitmap = 0;
427 			if (fabs(CH->LowPass-60.0)<0.01)
428 				bitmap = 1;
429 			else if (fabs(CH->LowPass-50.0)<0.01)
430 				bitmap = 2;
431 			else
432 				bitmap = 0;
433 			*(ptr+sectionStart+curSectLen) = 29;	// tag
434 			leu16a(1, ptr+sectionStart+curSectLen+1);	// length
435 			*(ptr+sectionStart+curSectLen+3) = bitmap;
436 			curSectLen += 4;
437 
438 			}
439 
440 			// Tag 32 (len = 5)
441 			if (VERBOSE_LEVEL>7) fprintf(stdout,"Section 1 Tag 32 \n");
442 
443 			*(ptr+sectionStart+curSectLen) = 32;	// tag
444 			leu16a(2, ptr+sectionStart+curSectLen+1);	// length
445 			if (hdr->Patient.Impairment.Heart==1) {
446 				*(ptr+sectionStart+curSectLen+3) = 0;
447 				*(ptr+sectionStart+curSectLen+4) = 1; 	// Apparently healthy
448 				curSectLen += 5;
449 			}
450 			else if (hdr->Patient.Impairment.Heart==3) {
451 				*(ptr+sectionStart+curSectLen+3) = 0;
452 				*(ptr+sectionStart+curSectLen+4) = 42; 	// Implanted cardiac pacemaker
453 				curSectLen += 5;
454 			}
455 
456 			// Tag 34 (len = 5)
457 			*(ptr+sectionStart+curSectLen) = 34;	// tag
458 			leu16a(5, ptr+sectionStart+curSectLen+1);	// length
459 			lei16a(hdr->tzmin, ptr+sectionStart+curSectLen+3);
460 			lei16a(0, ptr+sectionStart+curSectLen+5);
461 			curSectLen += 8;
462 
463 			// Tag 255 (len = 0)
464 			*(ptr+sectionStart+curSectLen) = 255;	// tag
465 			leu16a(0, ptr+sectionStart+curSectLen+1);	// length
466 			curSectLen += 3;
467 
468 			// Evaluate the size and correct it if odd
469 			if (curSectLen & 1) {
470 				*(ptr+sectionStart+curSectLen++) = 0;
471 			}
472 
473 			if (VERBOSE_LEVEL>7) fprintf(stdout,"End-of-Section %i %p\n",curSect,ptr);
474 
475 		}
476 		else if (curSect==2)  // SECTION 2
477 		{
478 		}
479     		else if (curSect==3)  // SECTION 3
480 		{
481 			ptr = (uint8_t*)realloc(ptr,sectionStart+16+2+9*NS+1);
482 			PtrCurSect = ptr+sectionStart;
483 			curSectLen = 16; // current section length
484 
485 			// Number of leads enclosed
486 			*(ptr+sectionStart+curSectLen++) = NS;
487 
488 // ### Situations with reference beat subtraction are not supported
489 // Situations with not all the leads simultaneously recorded are not supported
490 // Situations number of leads simultaneouly recorded != total number of leads are not supported
491 // We assume all the leads are recorded simultaneously
492 			*(ptr+sectionStart+curSectLen++) = (NS<<3) | 0x04;
493 
494 			for (i = 0; i < hdr->NS; i++) {
495 				CHANNEL_TYPE *CH=hdr->CHANNEL+i;
496 				if (CH->OnOff != 1) continue;
497 
498 				leu32a(1L, ptr+sectionStart+curSectLen);
499 				leu32a(hdr->data.size[0], ptr+sectionStart+curSectLen+4);
500 				*(ptr+sectionStart+curSectLen+8) = (uint8_t)CH->LeadIdCode;
501 				curSectLen += 9;
502 			}
503 
504 			// Evaluate the size and correct it if odd
505 			if ((curSectLen % 2) != 0) {
506 				*(ptr+sectionStart+curSectLen++) = 0;
507 			}
508 			memset(ptr+sectionStart+10,0,6); // reserved
509 		}
510 		else if (curSect==4)  // SECTION 4
511 		{
512 		}
513 		else if (curSect==5)  // SECTION 5
514 		{
515 			curSectLen = 0; // current section length
516 			aECG->Section5.StartPtr = sectionStart;
517 			aECG->Section5.Length = curSectLen;
518 		}
519 		else if (curSect==6)  // SECTION 6
520 		{
521 		    if (versionSection < 25)  // SECTION 6
522 		    {
523 			uint16_t GDFTYP = 3;
524 			size_t SZ = GDFTYP_BITS[GDFTYP]>>3;
525 			typeof(hdr->NS) i=0;
526 			for (i = 0; i < hdr->NS; i++) {
527 				CHANNEL_TYPE *hc=hdr->CHANNEL+i;
528 				if (hc->OnOff != 1) continue;
529 				hc->GDFTYP = GDFTYP;
530 			}
531 			ptr = (uint8_t*)realloc(ptr,sectionStart+16+6+2*NS+SZ*(hdr->data.size[0]*hdr->data.size[1]));
532 
533 			PtrCurSect = ptr+sectionStart;
534 			curSectLen = 16; // current section length
535 
536 			// Create all the fields
537 			char flagfirst = 1;
538 			for (i = 0; i < hdr->NS; i++) {
539 				CHANNEL_TYPE *CH=hdr->CHANNEL+i;
540 				if (CH->OnOff != 1) continue;
541 
542 				// check for physical dimension and adjust scaling factor to "nV"
543 				avm = CH->Cal * 1e9 * PhysDimScale(CH->PhysDimCode);
544 				if (flagfirst) {
545 					// Amplitude Value Multiplier (AVM)
546 					AVM = avm;
547 					flagfirst=0;
548 					continue;
549 				}
550 
551 				// check whether all channels have the same scaling factor
552 				if (fabs((AVM - avm)/AVM) > 1e-14)
553 					fprintf(stderr,"Warning SOPEN (SCP-WRITE): scaling factors differ between channel #1 and #%i. Scaling factor of 1st channel is used.\n",i+1);
554 			};
555 			avm16 = lrint(AVM);
556 			leu16a(avm16, ptr+sectionStart+curSectLen);
557 			avm = leu16p(ptr+sectionStart+curSectLen);
558 			curSectLen += 2;
559 			if (fabs((AVM - avm)/AVM)>1e-14)
560 				fprintf(stderr,"Warning SOPEN (SCP-WRITE): Scaling factor has been truncated (%f instead %f).\n",avm,AVM);
561 
562 			// Sample interval
563 			AVM = 1e6/hdr->SampleRate;
564 			avm16 = lrint(AVM);
565 			leu16a(avm16, ptr+sectionStart+curSectLen);
566 			avm = leu16p(ptr+sectionStart+curSectLen);
567 			curSectLen += 2;
568 			if (fabs((AVM - avm)/AVM)>1e-14)
569 				fprintf(stderr,"Warning SOPEN (SCP-WRITE): Sampling interval has been truncated (%f instead %f us).\n",avm,AVM);
570 
571 			// Diff used
572 			*(ptr+sectionStart+curSectLen++) = 0;
573 
574 			// Bimodal/Non-bimodal
575 			*(ptr+sectionStart+curSectLen++) = 0;
576 
577 
578 			/* DATA COMPRESSION
579 			    currently, no compression method is supported. In case of data compression, the
580 			    data compression can happen here.
581 			*/
582 
583 			// Fill the length block
584 			for (i = 0; i < NS; i++) {
585 				leu16a((uint16_t)hdr->data.size[0]*2, ptr+sectionStart+curSectLen);
586 				avm = leu16p(ptr+sectionStart+curSectLen);
587 				AVM = hdr->data.size[0]*2;
588 				if (fabs((AVM - avm)/AVM)>1e-14)
589 					fprintf(stderr,"Warning SOPEN (SCP-WRITE): Block length truncated (%f instead %f us).\n",avm,AVM);
590 				curSectLen += 2;
591 			}
592 
593 			/* data in channel multiplexed order */
594 			for (i = 0; i < hdr->NS; i++) {
595 				hdr->CHANNEL[i].SPR *= hdr->NRec;
596 			};
597 			hdr->NRec = 1;
598 
599 			// Prepare filling the data block with the ECG samples by SWRITE
600 			curSectLen += SZ * (hdr->data.size[hdr->FLAG.ROW_BASED_CHANNELS] * NS);
601 
602 			// Evaluate the size and correct it if odd
603 			if ((curSectLen % 2) != 0) {
604 				fprintf(stderr,"Warning Section 6 has an odd length\n");
605 				*(ptr+sectionStart+curSectLen++) = 0;
606 			}
607 
608 			memset(ptr+sectionStart+10,0,6); // reserved
609 			aECG->Section6.StartPtr = sectionStart;
610 			aECG->Section6.Length = curSectLen;
611 		    }
612 		}
613 		else if (curSect==7)  // SECTION 7
614 		{
615 			if (hdr->SCP.Section7 != NULL) {
616 				curSectLen = hdr->SCP.Section7Length+16; // current section length
617 				ptr = (uint8_t*)realloc(ptr,sectionStart+curSectLen);
618 				PtrCurSect = ptr+sectionStart;
619 				memcpy(PtrCurSect+16,hdr->SCP.Section7,hdr->SCP.Section7Length);
620 			}
621 		}
622 		else if (curSect==8)  // SECTION 8
623 		{
624 			if (hdr->SCP.Section8 != NULL) {
625 				curSectLen = hdr->SCP.Section8Length+16; // current section length
626 				ptr = (uint8_t*)realloc(ptr,sectionStart+curSectLen);
627 				PtrCurSect = ptr+sectionStart;
628 				memcpy(PtrCurSect,hdr->SCP.Section8,hdr->SCP.Section8Length);
629 			}
630 		}
631 		else if (curSect==9)  // SECTION 9
632 		{
633 			if (hdr->SCP.Section9 != NULL) {
634 				curSectLen = hdr->SCP.Section9Length+16; // current section length
635 				ptr = (uint8_t*)realloc(ptr,sectionStart+curSectLen);
636 				PtrCurSect = ptr+sectionStart;
637 				memcpy(PtrCurSect,hdr->SCP.Section9,hdr->SCP.Section9Length);
638 			}
639 		}
640 		else if (curSect==10)  // SECTION 10
641 		{
642 			if (hdr->SCP.Section10 != NULL) {
643 				curSectLen = hdr->SCP.Section10Length+16; // current section length
644 				ptr = (uint8_t*)realloc(ptr,sectionStart+curSectLen);
645 				PtrCurSect = ptr+sectionStart;
646 				memcpy(PtrCurSect+16,hdr->SCP.Section10,hdr->SCP.Section10Length);
647 			}
648 		}
649 		else if (curSect==11)  // SECTION 11
650 		{
651 			if (hdr->SCP.Section11 != NULL) {
652 				curSectLen = hdr->SCP.Section11Length+16; // current section length
653 				ptr = (uint8_t*)realloc(ptr,sectionStart+curSectLen);
654 				PtrCurSect = ptr+sectionStart;
655 				memcpy(PtrCurSect+16,hdr->SCP.Section11,hdr->SCP.Section11Length);
656 			}
657 		}
658 		else if (curSect==12) // SECTION 12
659 		{
660 		    if (versionSection > 25 ) // SECTION 12, SCP version 3
661 		    {
662 			uint16_t gdftyp= 0;
663 			uint8_t bps    = 0;
664 			//double PhysMax = -1.0/0.0;
665 			uint16_t PhysDimBaseCode = 4256;  // Volt
666 			for (k = 0; k < hdr->NS; k++) {
667 				CHANNEL_TYPE *hc = hdr->CHANNEL+k;
668 				if (hc->OnOff == 1) {
669 					if (bps < GDFTYP_BITS[gdftyp]) {
670 						bps = GDFTYP_BITS[hc->GDFTYP];
671 						gdftyp = hc->GDFTYP;
672 					}
673 				}
674 			}
675 
676 			if (NS>255) {
677 				NS=255;
678 				fprintf(stderr,"Warning SOPEN (SCP-WRITE): Number of channels exceeds 255, limited to 255.\n");
679 			}
680 
681 			bps = GDFTYP_BITS[gdftyp]>>3;
682 			double DigMax = ldexp(1.0, GDFTYP_BITS[gdftyp]-1)-1;
683 
684 			int ns=0;
685 			for (k=0; k < hdr->NS; k++) {
686 				CHANNEL_TYPE *hc = hdr->CHANNEL+k;
687 				if (hc->OnOff > 0) {
688 					if (ns > 255) {
689 						hc->OnOff = 0;
690 						continue;
691 					}
692 					ns++;
693 
694 					double scale = PhysDimScale(hc->PhysDimCode);
695 					hc->GDFTYP  =  gdftyp;
696 					hc->PhysMax =  max(fabs(hc->PhysMax),fabs(-hc->PhysMin))*scale*1e9;
697 					hc->PhysMin = -hc->PhysMax;
698 					hc->Off     =  0.0;
699 					hc->Cal     =  ceil(hc->PhysMax / hc->DigMax);	// AVM must be integer
700 					//hc->DigMax  =  hc->PhysMax / hc->Cal;
701 					hc->DigMin  = -hc->DigMax;
702 					hc->PhysDimCode = 4276;		// nV
703 				}
704 			}
705 
706 			curSectLen = 16 + 70 + NS * (4 + hdr->SPR*hdr->NRec * bps); // current section length without 16 bytes
707 			curSectLen+= (curSectLen & 1);
708 
709 			ptr        = (uint8_t*)realloc(ptr, sectionStart + curSectLen);
710 			memset(ptr + sectionStart, 0, 16+70);
711 
712 			PtrCurSect = ptr + sectionStart + 16;
713 			leu32a((uint32_t)hdr->SampleRate, PtrCurSect);
714 			PtrCurSect[4] = (uint8_t)NS;
715 			leu32a((uint32_t)(hdr->NRec*hdr->SPR), PtrCurSect + 5);
716 			PtrCurSect[9] = (uint8_t)bps;
717 
718 			// Recording Date and Time
719 			gdf_time T1 = hdr->T0;
720 			T0_tm = gdf_time2tm_time(hdr->T0);
721 			T0_tm->tm_gmtoff = hdr->tzmin*60;
722 
723 			leu16a(T0_tm->tm_year+1900, PtrCurSect+10);     // year
724 			PtrCurSect[12] = (uint8_t)(T0_tm->tm_mon + 1);  // month
725 			PtrCurSect[13] = (uint8_t)T0_tm->tm_mday; 	// day
726 			PtrCurSect[14] = (uint8_t)T0_tm->tm_hour;	// hour
727 			PtrCurSect[15] = (uint8_t)T0_tm->tm_min;	// minute
728 			PtrCurSect[16] = (uint8_t)T0_tm->tm_sec; 	// second
729 			// all other fields are set to zero. filter settings are defined in Section 1, Tags 27-29
730 
731 			/* Leads Definistion block */
732 			PtrCurSect += 70;
733 			for (k=0,ns=0; k < hdr->NS; k++) {
734 				CHANNEL_TYPE *hc = hdr->CHANNEL+k;
735 				if (hc->OnOff > 0) {
736 					PtrCurSect[ns*4]   = hc->LeadIdCode;
737 					leu16a(hc->Cal, PtrCurSect+1+ns*4);     // AVM per lead
738 					PtrCurSect[3+ns*4] = 0;
739 					ns++;
740 				}
741 			}
742 
743 			/* ECG signals data block */
744 			PtrCurSect += 4*NS;
745 		    }
746 		    aECG->Section12.StartPtr = sectionStart;
747 		    aECG->Section12.Length   = curSectLen;
748 		}
749 		else {
750 		}
751 
752 		// write to pointer field in Section 0
753 		leu16a(curSect, ptr+curSect*10+6+16); //
754 		leu32a(curSectLen, ptr+curSect*10+6+16+2); // length
755 		// Section start - must be odd. See EN1064:2005(E) Section 5.2.1
756 
757 		// write to Section ID Header
758 		if (curSectLen>0) {
759 			// Section 0: startpos in pointer field
760 			leu32a(sectionStart+1, ptr+curSect*10+6+16+6);
761 
762 			// Section ID header (16 bytes)
763 			leu16a(curSect, ptr+sectionStart+2); 	// Section ID
764 			leu32a(curSectLen, ptr+sectionStart+4); 	// section length->section header
765 			ptr[sectionStart+8] = versionSection; 	// Section Version Number
766 			ptr[sectionStart+9] = versionProtocol; 	// Protocol Version Number
767 			if (curSect==0) {
768 				memcpy(ptr+16,"SCPECG",6);	// defined as in ISO/DIS 11073-91064 Section 5.3.2
769 			}
770 			else {
771 				memset(ptr+sectionStart+10,0,6); // reserved according to ISO/DIS 11073-91064 Section 5.2.7
772 			}
773 			crc = CRCEvaluate(ptr+sectionStart+2,curSectLen-2); // compute CRC
774 			leu16a(crc, ptr+sectionStart);
775 		}
776 		sectionStart += curSectLen;	// offset for next section
777 	}
778 
779 	if (VERBOSE_LEVEL>7) fprintf(stdout,"SOPEN_SCP_WRITE 300\n");
780 
781 	// Prepare filling the data block with the ECG samples by SWRITE
782 	if (versionSection < 25)
783 		hdr->AS.rawdata = ptr+aECG->Section6.StartPtr+16+6+2*NS;
784 	else
785 		hdr->AS.rawdata = ptr+aECG->Section12.StartPtr+16+70+4*NS;
786 
787 	hdr->AS.Header = ptr;
788 
789 	if (VERBOSE_LEVEL>7) fprintf(stdout,"%s (line %i): %i,%i %i,%i \n",__func__,__LINE__,(int)aECG->Section6.StartPtr,(int)aECG->Section6.Length,(int)aECG->Section12.StartPtr,(int)aECG->Section12.Length);
790 
791 	return(0);
792 }
793 
794 #ifdef __cplusplus
795 }
796 #endif
797 
798