1function [argout,H1,h2] = hdr2ascii(source,dest)
2% HDR2ASCII converts the header information into ASCII text.
3%
4%   HDR2ASCII(HDR [, ...]);
5%	converts file header HDR
6%   HDR2ASCII(file [, ...]);
7%	converts header of file
8%   HDR2ASCII(arg,dest_file);
9%	converts file header HDR and writes it into dest_file
10%   HDR=HDR2ASCII(...);
11%	returns header HDR
12%
13% see also: SLOAD, SOPEN
14
15%  Copyright (C) 2007,2008,2020 by Alois Schloegl <alois.schloegl@gmail.com>
16%  This is part of the BIOSIG-toolbox http://biosig.sf.net/
17%
18% This program is free software; you can redistribute it and/or
19% modify it under the terms of the GNU General Public License
20% as published by the Free Software Foundation; either version 3
21% of the License, or (at your option) any later version.
22%
23% This program is distributed in the hope that it will be useful,
24% but WITHOUT ANY WARRANTY; without even the implied warranty of
25% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26% GNU General Public License for more details.
27%
28% You should have received a copy of the GNU General Public License
29% along with this program; if not, write to the Free Software
30% Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
31
32if nargin<2,
33	if isstruct(source)
34		HDR = source;
35	elseif ischar(source)
36		if any(source=='*')
37			[s,HDR]=sload(source);
38		else
39			HDR = sopen(source);
40			HDR = sclose(HDR);
41		end;
42	else
43		'not implemented yet',
44	end;
45	dest = [tempname,'.dlm'];
46elseif isstruct(source) && ischar(dest);
47	HDR = source;
48elseif ischar(source) && ischar(dest);
49	HDR = sopen(source);
50	HDR = sclose(HDR);
51else
52	'not implemented yet',
53end;
54if isnan(HDR.NS) && exist('mexSLOAD','file'),
55	[s,HDR]=mexSLOAD(HDR.FileName);
56end
57
58if nargin>1,
59	fid = fopen(dest,'wt');
60	if fid<0,
61		fprintf(2,'ERROR HDR2ASCII: could not open file %s\n',dest);
62		return;
63	end;
64else
65	fid = 1;
66end;
67
68
69%%%%%%%%% FIXED HEADER %%%%%%%%%%%%%%
70fprintf(fid,'[BioSig Header]\n\n');
71fprintf(fid,'Version=0.10\n');
72fprintf(fid,'generated=%04i-%02i-%02i %02i:%02i:%04.1f\n',datevec(now));
73if fid>2;
74	fprintf(fid,'\n;This is a TAB-delimiter file. When you edit this file, make sure not to corrupt the TABs ASCII(9)!\n\n');
75	fprintf(fid,'ThisFile=%s\n',dest);
76end;
77
78
79fprintf(fid,'\n[Fixed Header]\n');
80if isfield(HDR,'FileName')
81	fprintf(fid,'Filename\t= %s\n',HDR.FileName);
82end;
83fprintf(fid,'Format  \t= %s\n',HDR.TYPE);
84if isfield(HDR,'FILE') && isfield(HDR.FILE,'size'), fprintf(fid,'SizeOfFile\t= %i\n',HDR.FILE.size); end;
85if ~isfield(HDR,'NS')
86	HDR.NS = 0;
87end;
88fprintf(fid,'NumberOfChannels\t= %i\n',HDR.NS);
89if isfield(HDR,'SampleRate')
90	fprintf(fid,'SamplingRate    \t= %f (%i%+g) Hz\n',HDR.SampleRate, round(HDR.SampleRate), HDR.SampleRate-round(HDR.SampleRate));
91end;
92if isfield(HDR,'NRec') && isfield(HDR,'SPR')
93	fprintf(fid,'Number_of_Samples\t= %i\n',HDR.NRec*HDR.SPR);
94end;
95T0 = zeros(1,6);
96if isfield(HDR,'T0')
97	switch length(HDR.T0)
98	case 1, T0 = datevec(HDR.T0);
99	case 6, T0 = HDR.T0;
100	end;
101	fprintf(fid,'RecordingDateTime\t= %04i-%02i-%02i %02i:%02i:%06.3f\n',T0);
102end;
103if isfield(HDR,'Patient')
104	fprintf(fid,'Patient.\n');
105	if isfield(HDR.Patient,'Name')
106		fprintf(fid,'\tName      \t= %s\n',HDR.Patient.Name);
107	end;
108	if isfield(HDR.Patient,'Id')
109		fprintf(fid,'\tId\t\t= %s\n',HDR.Patient.Id);
110	end;
111	if isfield(HDR.Patient,'Sex')
112		if (HDR.Patient.Sex==1)
113			fprintf(fid,'\tGender   \t= male\n');
114		elseif (HDR.Patient.Sex==2)
115			fprintf(fid,'\tGender   \t= female\n');
116		else
117			fprintf(fid,'\tGender   \t= unknown\n');
118		end;
119	end;
120	T1 = zeros(1,6);
121	if isfield(HDR.Patient,'Birthday')
122		switch length(HDR.Patient.Birthday)
123		case 1,    T1 = datevec(HDR.Patient.Birthday);
124		case 6,    T1 = HDR.Patient.Birthday;
125		end;
126		if ~any(isnan(T0))
127			fprintf(fid,'\tAge\t\t= %4.1f years\n',(datenum(T0)-datenum(T1))/(365.25));
128		end;
129		fprintf(fid,'\tBirthday\t= %04i-%02i-%02i %02i:%02i:%06.3f\n',T1);
130	end;
131end;
132
133if isfield(HDR,'Manufacturer')
134	fprintf(fid,'Manufacturer.\n');
135	if isfield(HDR.Manufacturer,'Name')
136		fprintf(fid,'\tName\t\t= %s\n',HDR.Manufacturer.Name);
137	end;
138	if isfield(HDR.Manufacturer,'Model')
139		fprintf(fid,'\tModel\t\t= %s\n',HDR.Manufacturer.Model);
140	end;
141	if isfield(HDR.Manufacturer,'Version')
142		fprintf(fid,'\tVersion \t= %s\n',HDR.Manufacturer.Version);
143	end;
144	if isfield(HDR.Manufacturer,'SerialNumber')
145		fprintf(fid,'\tSerialNumber \t= %s\n',HDR.Manufacturer.SerialNumber);
146	end;
147end;
148
149
150%%%%%%%% CHANNEL DATA %%%%%%%%%%%%%%%
151if ~isfield(HDR,'AS') && isfield(HDR,'SampleRate')
152	HDR.AS.SampleRate = repmat(HDR.SampleRate,HDR.NS,1);
153end;
154if ~isfield(HDR.AS,'SPR'),
155	HDR.AS.SPR = repmat(HDR.SPR,1,HDR.NS);
156end;
157if ~isfield(HDR.AS,'SampleRate'),
158	HDR.AS.SampleRate = HDR.AS.SPR/HDR.SPR*HDR.SampleRate;
159end;
160if ~isfield(HDR,'THRESHOLD')
161	HDR.THRESHOLD = repmat(NaN,HDR.NS,2);
162end;
163if ~isfield(HDR,'PhysDimCode')
164	if isfield(HDR,'PhysDim')
165		HDR.PhysDimCode = physicalunits(HDR.PhysDim);
166	else
167		HDR.PhysDimCode = zeros(1,HDR.NS);
168	end
169end;
170if ~isfield(HDR,'LeadIdCode')
171	HDR = leadidcodexyz(HDR);
172end;
173if ~isfield(HDR,'REC')
174	HDR.REC.Impedance = repmat(NaN,HDR.NS,1);
175end;
176if ~isfield(HDR.REC,'Impedance')
177	HDR.REC.Impedance = repmat(NaN,HDR.NS,1);
178end;
179if ~isfield(HDR,'InChanSelect')
180	InChanSelect = 1:HDR.NS;
181else
182	InChanSelect = HDR.InChanSelect;
183end
184if ~isfield(HDR,'Filter') || ~isfield(HDR.Filter,'HighPass');
185	HDR.Filter.HighPass = repmat(NaN,1,HDR.NS);
186end;
187if ~isfield(HDR.Filter,'LowPass');
188	HDR.Filter.LowPass = repmat(NaN,1,HDR.NS);
189end;
190if ~isfield(HDR.Filter,'Notch');
191	HDR.Filter.Notch = repmat(NaN,1,HDR.NS);
192end;
193if (HDR.NS>0);
194if length(HDR.Filter.HighPass)==1,
195	HDR.Filter.HighPass = repmat(HDR.Filter.HighPass,HDR.NS,1);
196end;
197if length(HDR.Filter.LowPass)==1,
198	HDR.Filter.LowPass = repmat(HDR.Filter.LowPass,HDR.NS,1);
199end;
200if length(HDR.Filter.Notch)==1,
201	HDR.Filter.Notch = repmat(HDR.Filter.Notch,HDR.NS,1);
202end;
203end;
204
205if ~isfield(HDR,'Cal') && isfield(HDR,'Calib')
206	Cal = ones(HDR.NS,1);
207	Cal(InChanSelect) = diag(HDR.Calib(2:end,:));
208elseif isfield(HDR,'Cal')
209	if length(HDR.Cal)==1,
210		Cal = repmat(HDR.Cal,HDR.NS,1);
211	else
212		Cal = HDR.Cal;
213	end;
214elseif isfield(HDR,'DigMin') && isfield(HDR,'DigMax') && isfield(HDR,'PhysMin') && isfield(HDR,'PhysMax')
215	Cal = (HDR.PhysMax-HDR.PhysMin)./(HDR.DigMax-HDR.DigMin);
216else
217	Cal = repmat(NaN,1,HDR.NS);
218end;
219
220if ~isfield(HDR,'Off') && isfield(HDR,'Calib')
221	Off = zeros(HDR.NS,1);
222	Cal(InChanSelect) = diag(HDR.Calib(2:end,:));
223elseif isfield(HDR,'Off')
224	if length(HDR.Off)==1,
225		Off = repmat(HDR.Off,HDR.NS,1);
226	else
227		Off = HDR.Off;
228	end;
229elseif isfield(HDR,'DigMin') && isfield(HDR,'DigMax') && isfield(HDR,'PhysMin') && isfield(HDR,'PhysMax')
230	Off = HDR.PhysMin - Cal.*HDR.DigMin;
231else
232	Off = repmat(NaN,1,HDR.NS);
233end;
234
235PhysDim = physicalunits(HDR.PhysDimCode);
236fprintf(fid,'\n[Channel Header]\n#No  LeadId  Label\tfs [Hz]\tGDFTYP\tTH-  TH+  Offset  Calib  PhysDim  HP[Hz]  LP[Hz]  Notch  R[kOhm]  x  y  z\n');
237for k = 1:HDR.NS,
238	Label = HDR.Label{k};
239	Z = HDR.REC.Impedance(k)/1000;
240	gdftyp = HDR.GDFTYP(min(length(HDR.GDFTYP),k));
241	LP = HDR.Filter.LowPass(min(length(HDR.Filter.LowPass),k));
242	HP = HDR.Filter.HighPass(min(length(HDR.Filter.HighPass),k));
243	Notch = HDR.Filter.Notch(min(length(HDR.Filter.Notch),k));
244	physdim = PhysDim(min(length(PhysDim),k));
245	Label(Label==9)=' '; % replace TAB's because TAB's are used as field delimiter
246	if isfield(HDR.AS,'SampleRate')
247		Fs = HDR.AS.SampleRate(k);
248	else
249		Fs = HDR.SampleRate;
250	end;
251	fprintf(fid,'%3i  %i\t%-9s\t%6.1f %2i  %i\t%i\t%6e\t%6e %5s  %6.4f %5.1f  %i  %5.1f  %f %f %f\n',k,HDR.LeadIdCode(k),Label,Fs,gdftyp,HDR.THRESHOLD(k,1:2),Off(k),Cal(k),physdim{1},HP,LP,Notch,Z,HDR.ELEC.XYZ(k,:));
252end;
253
254if ~isfield(HDR.EVENT,'SampleRate');
255	HDR.EVENT.SampleRate = HDR.SampleRate;
256end;
257%%%%%%%%%% EVENTTABLE %%%%%%%%%%%%%%%
258fprintf(fid,'\n[Event Table]\n');
259fprintf(fid,'NumberOfEvents=%i  SampleRate=%f\n   TYP\t   POS     \t date         time\t',length(HDR.EVENT.POS),HDR.EVENT.SampleRate);
260if isfield(HDR.EVENT,'CHN')
261	fprintf(fid,'\tCHN\tDUR/VAL');
262end;
263fprintf(fid,'\tDescription\n');
264
265% use global to improve speed
266global BIOSIG_GLOBAL;
267if ~isfield(BIOSIG_GLOBAL,'ISLOADED_EVENTCODES')
268	BIOSIG_GLOBAL.ISLOADED_EVENTCODES = 0;
269end;
270if ~BIOSIG_GLOBAL.ISLOADED_EVENTCODES,
271	H=sopen('eventcodes.txt');	% if successful, it will set BIOSIG_GLOBAL.ISLOADED_EVENTCODES
272	sclose(H);
273	if ~BIOSIG_GLOBAL.ISLOADED_EVENTCODES,
274		fprintf(2,'Error: unable to load definition of EventCodes - in order to fix this, include the directory  .../biosig4matlab/doc/ in your path');
275	end
276end;
277
278
279[tmp,eIdx]=sort(HDR.EVENT.POS); % sort event in chronological order
280
281for k = eIdx(:)',
282	if any(isnan(HDR.T0))
283		T0 = 0;
284	elseif length(HDR.T0)==1,
285		T0 = HDR.T0;
286	else
287		T0 = datenum(HDR.T0);
288	end;
289	if isfield(HDR.EVENT,'TimeStamp');
290		t = datevec(HDR.EVENT.TimeStamp(k));
291	else
292		t = datevec(HDR.EVENT.POS(k)/(24*3600*HDR.SampleRate)+T0);
293	end
294	fprintf(fid,'0x%04x\t%9i\t%04i-%02i-%02i %02i:%02i:%07.4f',[HDR.EVENT.TYP(k),HDR.EVENT.POS(k),t(1:6)]);
295	if isfield(HDR.EVENT,'CHN')
296		if ~isempty(HDR.EVENT.CHN)
297			fprintf(fid,'\t%i', HDR.EVENT.CHN(k));
298			DUR = HDR.EVENT.DUR(k);
299			if (HDR.EVENT.TYP(k)~=hex2dec('7fff'))
300				fprintf(fid,'\t%i', DUR);
301			end;
302		end;
303	else
304		fprintf(fid,'\t-\t-');
305	end;
306
307	if HDR.EVENT.TYP(k)==hex2dec('7fff'),
308		ch = HDR.EVENT.CHN(k);
309		pu = physicalunits(HDR.PhysDimCode(ch));
310		fprintf(fid,'\t%f %s', HDR.EVENT.VAL(k), pu{1});
311	elseif 0,
312	elseif HDR.EVENT.TYP(k)==0,
313		;
314	elseif HDR.EVENT.TYP(k)==hex2dec('7ffe'),
315		;
316	elseif (isfield(HDR.EVENT,'CodeDesc') && (HDR.EVENT.TYP(k) <= length(HDR.EVENT.CodeDesc)))
317		fprintf(fid,'\t%s',HDR.EVENT.CodeDesc{HDR.EVENT.TYP(k)});
318	else
319		ix = find(HDR.EVENT.TYP(k)==BIOSIG_GLOBAL.EVENT.CodeIndex);
320		if length(ix)==1,
321			fprintf(fid,'\t%s',BIOSIG_GLOBAL.EVENT.CodeDesc{ix});
322		end;
323	end;
324	fprintf(fid,'\n');
325end;
326
327if fid>2,
328	fclose(fid);
329end;
330if nargout>0,
331	argout=HDR;
332end;
333
334