TPCCLIB
Loading...
Searching...
No Matches
taccat.c
Go to the documentation of this file.
1
8/*****************************************************************************/
9#include "tpcclibConfig.h"
10/*****************************************************************************/
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include <math.h>
15#include <unistd.h>
16/*****************************************************************************/
17#include "tpcextensions.h"
18#include "tpcift.h"
19#include "tpctac.h"
20/*****************************************************************************/
21
22/*****************************************************************************/
23static char *info[] = {
24 "Catenate the TACs from the second TAC file to the first one, or to a new",
25 "datafile. Sample times or correction for physical decay are not changed.",
26 "Each TAC file must contain the same number of TACs, and if more than one TAC,",
27 "TAC names must match, too.",
28 " ",
29 "Usage: @P [options] tacfile1 tacfile2 [catenated_file]",
30 " ",
31 "Options:",
32 " -both | -first | -second | -cut=<time>",
33 " In case of overlapping samples, either samples from both (-both),",
34 " first (-first), or second (-second, default) TAC are saved in",
35 " combined file, or specified cut time is used.",
36 " Cut time can also be given in file with keys 'x' or 'time'.",
37 " By default, overlap will lead to an error.",
38 " --force",
39 " Program does not mind if the time or calibration units cannot be",
40 " converted to match, or if TAC names do not match.",
41 " -stdoptions", // List standard options like --help, -v, etc
42 " ",
43 "Example:",
44 " @P t455ap_pump.kbq t455ap_manual.kbq t455ap_combined.kbq",
45 " ",
46 "See also: tacblend, taccut, tacadd0, tacadd, tacunit, tactime, taccross",
47 " ",
48 "Keywords: TAC, tool, input, blood",
49 0};
50/*****************************************************************************/
51
52/*****************************************************************************/
53/* Turn on the globbing of the command line, since it is disabled by default in
54 mingw-w64 (_dowildcard=0); in MinGW32 define _CRT_glob instead, if necessary;
55 In Unix&Linux wildcard command line processing is enabled by default. */
56/*
57#undef _CRT_glob
58#define _CRT_glob -1
59*/
60int _dowildcard = -1;
61/*****************************************************************************/
62
63/*****************************************************************************/
65enum {OVL_ERROR, OVL_USE_BOTH, OVL_USE_FIRST, OVL_USE_SECOND, OVL_CUTTIME};
66
70int main(int argc, char **argv)
71{
72 int ai, help=0, version=0, verbose=1;
73 char tacfile1[FILENAME_MAX], tacfile2[FILENAME_MAX], outfile[FILENAME_MAX];
74 int forceMode=0;
75 int overlapMode=OVL_ERROR;
76 double cutTime=nan("");
77
78
79
80 /*
81 * Get arguments
82 */
83 if(argc==1) {tpcPrintUsage(argv[0], info, stderr); return(1);}
84 tacfile1[0]=tacfile2[0]=outfile[0]=(char)0;
85
86 /* Options */
87 for(ai=1; ai<argc; ai++) if(*argv[ai]=='-') {
88 if(tpcProcessStdOptions(argv[ai], &help, &version, &verbose)==0) continue;
89 char *cptr=argv[ai]+1; if(*cptr=='-') cptr++; if(!*cptr) continue;
90 if(strcasecmp(cptr, "F")==0 || strcasecmp(cptr, "FORCE")==0) {
91 forceMode=1; continue;
92 } else if(strncasecmp(cptr, "BOTH", 1)==0) {
93 overlapMode=OVL_USE_BOTH; continue;
94 } else if(strncasecmp(cptr, "FIRST", 2)==0) {
95 overlapMode=OVL_USE_FIRST; continue;
96 } else if(strncasecmp(cptr, "SECOND", 2)==0) {
97 overlapMode=OVL_USE_SECOND; continue;
98 } else if(strncasecmp(cptr, "CUT=", 4)==0) {
99 overlapMode=OVL_CUTTIME;
100 cptr+=4;
101 /* Try as a value first */
102 cutTime=atofVerified(cptr);
103 if(cutTime>0.0) continue;
104 /* Then try as a IFT file */
105 {
106 FILE *fp=fopen(cptr, "r");
107 if(fp==NULL) {fprintf(stderr, "Error: invalid cut time.\n"); return(1);}
108 IFT ift; iftInit(&ift);
109 int ret=iftRead(&ift, fp, 1, 0, NULL);
110 fclose(fp);
111 if(ret==0) {
112 if(iftGetDoubleValue(&ift, "x", 0, &cutTime)>=0 && cutTime>0.0) {iftFree(&ift); continue;}
113 if(iftGetDoubleValue(&ift, "time", 0, &cutTime)>=0 && cutTime>0.0) {iftFree(&ift); continue;}
114 if(iftGetDoubleValue(&ift, "cut", 0, &cutTime)>=0 && cutTime>0.0) {iftFree(&ift); continue;}
115 iftFree(&ift);
116 }
117 }
118 fprintf(stderr, "Error: invalid cut time.\n"); return(1);
119 }
120 fprintf(stderr, "Error: invalid option '%s'\n", argv[ai]);
121 return(1);
122 } else break; // tac name argument may start with '-'
123
124 TPCSTATUS status; statusInit(&status);
125 statusSet(&status, __func__, __FILE__, __LINE__, TPCERROR_OK);
126 status.verbose=verbose-3;
127
128 /* Print help or version? */
129 if(help==2) {tpcHtmlUsage(argv[0], info, ""); return(0);}
130 if(help) {tpcPrintUsage(argv[0], info, stdout); return(0);}
131 if(version) {tpcPrintBuild(argv[0], stdout); return(0);}
132
133 /* Process other arguments, starting from the first non-option */
134 if(ai<argc) strlcpy(tacfile1, argv[ai++], FILENAME_MAX);
135 if(ai<argc) strlcpy(tacfile2, argv[ai++], FILENAME_MAX);
136 if(ai<argc) strlcpy(outfile, argv[ai++], FILENAME_MAX);
137 if(ai<argc) {fprintf(stderr, "Error: invalid argument '%s'.\n", argv[ai]); return(1);}
138
139 /* Is something missing? */
140 if(!tacfile2[0]) {tpcPrintUsage(argv[0], info, stdout); return(1);}
141 if(!outfile[0]) strcpy(outfile, tacfile1);
142 if(overlapMode==OVL_CUTTIME && !(cutTime>0.0)) {fprintf(stderr, "Error: invalid cut time.\n"); return(1);}
143
144 /* In verbose mode print arguments and options */
145 if(verbose>1) {
146 for(ai=0; ai<argc; ai++) printf("%s ", argv[ai]);
147 printf("\n");
148 printf("tacfile1 := %s\n", tacfile1);
149 printf("tacfile2 := %s\n", tacfile2);
150 printf("outfile := %s\n", outfile);
151 printf("overlapMode := %d\n", overlapMode);
152 if(overlapMode==OVL_CUTTIME) printf("cutTime := %g\n", cutTime);
153 printf("forceMode := %d\n", forceMode);
154 fflush(stdout);
155 }
156
157
158 /*
159 * Read the files
160 */
161 TAC tac1, tac2;
162 tacInit(&tac1); tacInit(&tac2);
163 if(verbose>1) printf("reading %s\n", tacfile1);
164 if(tacRead(&tac1, tacfile1, &status)!=TPCERROR_OK) {
165 fprintf(stderr, "Error: %s\n", errorMsg(status.error));
166 tacFree(&tac1); tacFree(&tac2); return(2);
167 }
168 if(verbose>2) {
169 printf("fileformat := %s\n", tacFormattxt(tac1.format));
170 printf("tacNr := %d\n", tac1.tacNr);
171 printf("sampleNr := %d\n", tac1.sampleNr);
172 printf("xunit := %s\n", unitName(tac1.tunit));
173 printf("yunit := %s\n", unitName(tac1.cunit));
174 }
175 if(verbose>1) printf("reading %s\n", tacfile2);
176 if(tacRead(&tac2, tacfile2, &status)!=TPCERROR_OK) {
177 fprintf(stderr, "Error: %s\n", errorMsg(status.error));
178 tacFree(&tac1); tacFree(&tac2); return(3);
179 }
180 if(verbose>2) {
181 printf("fileformat := %s\n", tacFormattxt(tac2.format));
182 printf("tacNr := %d\n", tac2.tacNr);
183 printf("sampleNr := %d\n", tac2.sampleNr);
184 printf("xunit := %s\n", unitName(tac2.tunit));
185 printf("yunit := %s\n", unitName(tac2.cunit));
186 }
187
188 /* Check if the TAC number is different */
189 if(verbose>1) printf("checking data...\n");
190 if(tac1.tacNr!=tac2.tacNr) {
191 fprintf(stderr, "Error: different number of TACs.\n");
192 tacFree(&tac1); tacFree(&tac2); return(4);
193 }
194
195 /* Check that TAC names match, if more than one TAC */
196 if(tac1.tacNr>1 && tacCompareNames(&tac1, &tac2, -1, &status)!=0) {
197 if(forceMode) {
198 fprintf(stderr, "Warning: TAC names do not match.\n");
199 } else {
200 /* Without force mode this is an error, unless sorting helps */
201 tacSortByName(&tac1, NULL);
202 tacSortByName(&tac2, NULL);
203 if(tacCompareNames(&tac1, &tac2, -1, &status)!=0) {
204 fprintf(stderr, "Error: TAC names do not match.\n");
205 tacFree(&tac1); tacFree(&tac2); return(4);
206 }
207 fprintf(stderr, "Note: TACs sorted by name.\n");
208 }
209 }
210
211 /* Delete missing samples */
213 if(tacNotNaNs(&tac1, -1)<1) {
214 fprintf(stderr, "Error: no valid samples in %s\n", tacfile1);
215 tacFree(&tac1); tacFree(&tac2); return(2);
216 }
218 if(tacNotNaNs(&tac2, -1)<1) {
219 fprintf(stderr, "Error: no valid samples in %s\n", tacfile2);
220 tacFree(&tac1); tacFree(&tac2); return(3);
221 }
222
223 /* Sort by sample times */
224 tacSortByTime(&tac1, NULL);
225 tacSortByTime(&tac2, NULL);
226
227 /* Check time units */
228 if(tac1.tunit==UNIT_UNKNOWN || tac2.tunit==UNIT_UNKNOWN) {
229 fprintf(stderr, "Warning: unknown time units.\n");
230 if(forceMode) { // In force mode use what we got
231 if(tac1.tunit!=UNIT_UNKNOWN) tac2.tunit=tac1.tunit;
232 else if(tac2.tunit!=UNIT_UNKNOWN) tac1.tunit=tac2.tunit;
233 }
234 }
235 if(tac2.tunit!=UNIT_UNKNOWN) {
236 /* Try to convert time units to time units in the first file */
237 int ret=tacXUnitConvert(&tac2, tac1.tunit, &status);
238 if(ret!=TPCERROR_OK) {
239 if(verbose>2) fprintf(stderr, "Status: %s\n", errorMsg(status.error));
240 if(forceMode) {
241 fprintf(stderr, "Warning: non-compatible TAC time units.\n");
242 } else {
243 fprintf(stderr, "Error: non-compatible TAC time units.\n");
244 tacFree(&tac1); tacFree(&tac2); return(4);
245 }
246 }
247 }
248 /* If either TAC file only has frame mid times, then both have */
249 if(tac1.isframe==0) tac2.isframe=0;
250 else if(tac2.isframe==0) tac1.isframe=0;
251
252 /* Check concentration units */
253 if(tac1.cunit==UNIT_UNKNOWN || tac2.cunit==UNIT_UNKNOWN) {
254 fprintf(stderr, "Warning: unknown concentration units.\n");
255 if(forceMode) { // In force mode use what we got
256 if(tac1.cunit!=UNIT_UNKNOWN) tac2.cunit=tac1.cunit;
257 else if(tac2.cunit!=UNIT_UNKNOWN) tac1.cunit=tac2.cunit;
258 }
259 }
260 if(tac2.cunit!=UNIT_UNKNOWN) {
261 /* Try to convert concentration units to units in the first file */
262 int ret=tacYUnitConvert(&tac2, tac1.cunit, &status);
263 if(ret!=TPCERROR_OK) {
264 if(verbose>2) fprintf(stderr, "Status: %s\n", errorMsg(status.error));
265 if(forceMode) {
266 fprintf(stderr, "Warning: non-compatible TAC concentration units.\n");
267 } else {
268 fprintf(stderr, "Error: non-compatible TAC concentration units.\n");
269 tacFree(&tac1); tacFree(&tac2); return(4);
270 }
271 }
272 }
273
274 /* If one of TAC files only has frame mid times, then we only use mid times */
275 if(tac1.isframe==0 || tac2.isframe==0) tac1.isframe=tac2.isframe=0;
276 /* Remove weighting unless both have similar weighting */
277 if(tac1.weighting!=tac2.weighting) tac1.weighting=tac2.weighting=WEIGHTING_OFF;
278
279
280 /*
281 * Check if there is overlap in sample times of the two files
282 */
283 if(verbose>1) printf("checking for time overlap\n");
284 int isOverlap=0;
285 double xmin1, xmax1, xmin2, xmax2, xmin, xmax;
286 if(tacXRange(&tac1, &xmin1, &xmax1) || tacXRange(&tac2, &xmin2, &xmax2)) {
287 fprintf(stderr, "Error: invalid sample times.\n");
288 tacFree(&tac1); tacFree(&tac2); return(5);
289 }
290 if(verbose>3) {
291 printf("x range 1 := %g - %g\n", xmin1, xmax1);
292 printf("x range 2 := %g - %g\n", xmin2, xmax2);
293 }
294 if(xmax1>xmin2) isOverlap=1;
295 else if(!tac1.isframe && xmax1>=xmin2) isOverlap=1;
296 if(isOverlap && overlapMode==OVL_ERROR) {
297 fprintf(stderr, "Error: overlapping sample times.\n");
298 tacFree(&tac1); tacFree(&tac2); return(5);
299 }
300 xmin=xmin1; if(xmin2<xmin) xmin=xmin2;
301 xmax=xmax1; if(xmax2>xmax) xmax=xmax2;
302 if(overlapMode==OVL_CUTTIME && (cutTime<xmin || cutTime>xmax)) {
303 fprintf(stderr, "Error: cut time outside of data time range %g - %g\n", xmin, xmax);
304 tacFree(&tac1); tacFree(&tac2); return(5);
305 }
306 if(isOverlap) {
307 int fail=0;
308 if(overlapMode==OVL_USE_FIRST && xmin1>xmin2) fail=1;
309 if(overlapMode==OVL_USE_SECOND && xmin2<=xmin1) fail=2;
310 if(overlapMode==OVL_CUTTIME && (xmin1>cutTime || xmax2<cutTime)) fail=3;
311 if(fail) {
312 fprintf(stderr, "Error: incompatible sample time ranges or wrong file order.\n");
313 tacFree(&tac1); tacFree(&tac2); return(5);
314 }
315 }
316 /* Do not care about overlap if all data is used anyway */
317 if(isOverlap) {
318 if(overlapMode==OVL_USE_BOTH) {
319 isOverlap=0;
320 fprintf(stderr, "Warning: overlap in sample times is ignored.\n");
321 } else {
322 fprintf(stderr, "Warning: overlap in sample times will be removed.\n");
323 }
324 }
325
326 /* Set cut time based on TAC preference, if that is given */
327// if(overlapMode==OVL_USE_FIRST) cutTime=xmax1;
328// if(overlapMode==OVL_USE_SECOND) cutTime=xmin2;
329 if(overlapMode==OVL_USE_FIRST) {cutTime=xmax1; if(xmax1==xmin2) cutTime+=1.0E-12;}
330 if(overlapMode==OVL_USE_SECOND) {cutTime=xmin2; if(xmax1==xmin2) cutTime-=1.0E-12;}
331 if(verbose>4 && isOverlap && (overlapMode==OVL_USE_FIRST || overlapMode==OVL_USE_SECOND))
332 printf("cutTime := %g\n", cutTime);
333
334
335 /* Delete overlapping part from first dataset, if necessary */
336 if(isOverlap && overlapMode!=OVL_USE_FIRST && xmax1>=cutTime) {
337 if(verbose>1) printf("deleting overlap from the first dataset\n");
338 if(tac1.isframe) {
339 for(int i=tac1.sampleNr-1; i>=0; i--)
340 if(tac1.x2[i]>cutTime) tacDeleteSample(&tac1, i);
341 } else {
342 for(int i=tac1.sampleNr-1; i>=0; i--)
343 if(tac1.x[i]>cutTime) tacDeleteSample(&tac1, i);
344 }
345 if(tac1.sampleNr<1 || tacXRange(&tac1, &xmin1, &xmax1)) {
346 fprintf(stderr, "Error: no data from file 1 included.\n");
347 tacFree(&tac1); tacFree(&tac2); return(5);
348 }
349 if(verbose>3) printf("final x range 1 := %g - %g\n", xmin1, xmax1);
350 }
351 /* Delete overlapping part from second dataset, if necessary */
352 if(isOverlap && overlapMode!=OVL_USE_SECOND && xmin2<=cutTime) {
353 if(verbose>1) printf("deleting overlap from the second dataset\n");
354 if(tac1.isframe) {
355 for(int i=tac2.sampleNr-1; i>=0; i--)
356 if(tac2.x1[i]<cutTime) tacDeleteSample(&tac2, i);
357 } else {
358 for(int i=tac2.sampleNr-1; i>=0; i--)
359 if(tac2.x[i]<cutTime) tacDeleteSample(&tac2, i);
360 }
361 if(tac2.sampleNr<1 || tacXRange(&tac2, &xmin2, &xmax2)) {
362 fprintf(stderr, "Error: no data from file 2 included.\n");
363 tacFree(&tac1); tacFree(&tac2); return(5);
364 }
365 if(verbose>3) printf("final x range 2 := %g - %g\n", xmin2, xmax2);
366 }
367
368
369 /*
370 * Add data from second file in to the first one
371 */
372 if(verbose>1) printf("combining data\n");
373 if(tacAllocateMoreSamples(&tac1, tac2.sampleNr)) {
374 fprintf(stderr, "Error: cannot allocate memory.\n");
375 tacFree(&tac1); tacFree(&tac2); return(6);
376 }
377 for(int i=0; i<tac2.sampleNr; i++) {
378 tac1.x[tac1.sampleNr]=tac2.x[i];
379 tac1.x1[tac1.sampleNr]=tac2.x1[i];
380 tac1.x2[tac1.sampleNr]=tac2.x2[i];
381 for(int j=0; j<tac1.tacNr; j++) tac1.c[j].y[tac1.sampleNr]=tac2.c[j].y[i];
382 tac1.w[tac1.sampleNr]=tac2.w[i];
383 tac1.sampleNr++;
384 }
385 /* Sort by sample time */
386 tacSortByTime(&tac1, NULL);
387
388 /* Add interfile type header contents that are stored as comments in file 2 */
389 iftCopyItems(&tac1.h, &tac2.h, 1, 1, 2, NULL);
390 /* Remove duplicate header fields */
391 iftDeleteDuplicateKeys(&tac1.h, NULL);
392
393 /* No need for the second dataset */
394 tacFree(&tac2);
395
396 /*
397 * Save data
398 */
399 if(verbose>1) printf("writing %s\n", outfile);
400 {
401 FILE *fp; fp=fopen(outfile, "w");
402 if(fp==NULL) {
403 fprintf(stderr, "Error: cannot open file for writing (%s)\n", outfile);
404 tacFree(&tac1); return(11);
405 }
406 int ret=tacWrite(&tac1, fp, TAC_FORMAT_UNKNOWN, 1, &status);
407 fclose(fp);
408 if(ret!=TPCERROR_OK) {
409 fprintf(stderr, "Error (%d): %s\n", ret, errorMsg(status.error));
410 tacFree(&tac1); return(12);
411 }
412 if(verbose>=0) {
413 printf("%d samples saved in %s.\n", tac1.sampleNr, outfile);
414 }
415 }
416 tacFree(&tac1);
417
418 return(0);
419}
420/*****************************************************************************/
421
422/*****************************************************************************/
double atofVerified(const char *s)
Definition decpoint.c:75
void iftFree(IFT *ift)
Definition ift.c:37
int iftCopyItems(IFT *ift1, IFT *ift2, int is_key_required, int is_value_required, int is_comment_accepted, TPCSTATUS *status)
Definition ift.c:386
void iftInit(IFT *ift)
Definition ift.c:21
int iftDeleteDuplicateKeys(IFT *ift, TPCSTATUS *status)
Definition ift.c:348
int iftGetDoubleValue(IFT *ift, const char *key, int index, double *v)
Definition iftfind.c:191
int iftRead(IFT *ift, FILE *fp, int is_key_required, int is_comment_accepted, TPCSTATUS *status)
Definition iftio.c:130
int tpcProcessStdOptions(const char *s, int *print_usage, int *print_version, int *verbose_level)
Definition proginfo.c:47
int tpcHtmlUsage(const char *program, char *text[], const char *path)
Definition proginfo.c:169
void tpcPrintBuild(const char *program, FILE *fp)
Definition proginfo.c:339
void tpcPrintUsage(const char *program, char *text[], FILE *fp)
Definition proginfo.c:114
void statusInit(TPCSTATUS *s)
Definition statusmsg.c:104
char * errorMsg(tpcerror e)
Definition statusmsg.c:68
void statusSet(TPCSTATUS *s, const char *func, const char *srcfile, int srcline, tpcerror error)
Definition statusmsg.c:142
size_t strlcpy(char *dst, const char *src, size_t dstsize)
Definition stringext.c:635
Definition tpcift.h:43
double * y
Definition tpctac.h:75
Definition tpctac.h:87
double * x
Definition tpctac.h:97
unit cunit
Definition tpctac.h:105
tacformat format
Definition tpctac.h:93
int sampleNr
Definition tpctac.h:89
IFT h
Optional (but often useful) header information.
Definition tpctac.h:141
double * w
Definition tpctac.h:111
int isframe
Definition tpctac.h:95
TACC * c
Definition tpctac.h:117
weights weighting
Definition tpctac.h:115
double * x2
Definition tpctac.h:101
unit tunit
Definition tpctac.h:109
double * x1
Definition tpctac.h:99
int tacNr
Definition tpctac.h:91
int verbose
Verbose level, used by statusPrint() etc.
tpcerror error
Error code.
void tacFree(TAC *tac)
Definition tac.c:106
void tacInit(TAC *tac)
Definition tac.c:24
int tacAllocateMoreSamples(TAC *tac, int addNr)
Allocate memory for more samples in TAC data.
Definition tac.c:435
int tacCompareNames(TAC *d1, TAC *d2, const int i, TPCSTATUS *status)
Definition taccomp.c:67
int tacRead(TAC *d, const char *fname, TPCSTATUS *status)
Definition tacio.c:413
char * tacFormattxt(tacformat c)
Definition tacio.c:98
int tacWrite(TAC *tac, FILE *fp, tacformat format, int extra, TPCSTATUS *status)
Definition tacio.c:332
int tacNotNaNs(TAC *tac, const int i)
Definition tacnan.c:84
int tacSortByTime(TAC *d, TPCSTATUS *status)
Definition tacorder.c:74
int tacSortByName(TAC *d, TPCSTATUS *status)
Definition tacorder.c:178
int tacYUnitConvert(TAC *tac, const int u, TPCSTATUS *status)
Definition tacunits.c:72
int tacXUnitConvert(TAC *tac, const int u, TPCSTATUS *status)
Definition tacunits.c:23
int tacDeleteMissingSamples(TAC *d)
Delete those samples (time frames) from TAC structure, which contain only missing y values,...
Definition tacx.c:450
int tacDeleteSample(TAC *d, int i)
Delete a certain sample (time frame) from TAC structure.
Definition tacx.c:426
int tacXRange(TAC *d, double *xmin, double *xmax)
Get the range of x values (times) in TAC structure.
Definition tacx.c:124
Header file for library libtpcextensions.
@ WEIGHTING_OFF
Not weighted or weights not available (weights for all included samples are 1.0).
@ UNIT_UNKNOWN
Unknown unit.
@ TPCERROR_OK
No error.
char * unitName(int unit_code)
Definition units.c:143
Header file for library libtpcift.
Header file for library libtpctac.
@ TAC_FORMAT_UNKNOWN
Unknown format.
Definition tpctac.h:28