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 FILE *fp=fopen(cptr, "r");
106 if(fp==NULL) {fprintf(stderr, "Error: invalid cut time.\n"); return(1);}
107 IFT ift; iftInit(&ift);
108 if(iftRead(&ift, fp, 1, 0, NULL)==0) {
109 if(iftGetDoubleValue(&ift, "x", 0, &cutTime)>=0 && cutTime>0.0) {iftFree(&ift); continue;}
110 if(iftGetDoubleValue(&ift, "time", 0, &cutTime)>=0 && cutTime>0.0) {iftFree(&ift); continue;}
111 if(iftGetDoubleValue(&ift, "cut", 0, &cutTime)>=0 && cutTime>0.0) {iftFree(&ift); continue;}
112 iftFree(&ift);
113 }
114 fprintf(stderr, "Error: invalid cut time.\n"); return(1);
115 }
116 fprintf(stderr, "Error: invalid option '%s'\n", argv[ai]);
117 return(1);
118 } else break; // tac name argument may start with '-'
119
120 TPCSTATUS status; statusInit(&status);
121 statusSet(&status, __func__, __FILE__, __LINE__, TPCERROR_OK);
122 status.verbose=verbose-3;
123
124 /* Print help or version? */
125 if(help==2) {tpcHtmlUsage(argv[0], info, ""); return(0);}
126 if(help) {tpcPrintUsage(argv[0], info, stdout); return(0);}
127 if(version) {tpcPrintBuild(argv[0], stdout); return(0);}
128
129 /* Process other arguments, starting from the first non-option */
130 if(ai<argc) strlcpy(tacfile1, argv[ai++], FILENAME_MAX);
131 if(ai<argc) strlcpy(tacfile2, argv[ai++], FILENAME_MAX);
132 if(ai<argc) strlcpy(outfile, argv[ai++], FILENAME_MAX);
133 if(ai<argc) {fprintf(stderr, "Error: invalid argument '%s'.\n", argv[ai]); return(1);}
134
135 /* Is something missing? */
136 if(!tacfile2[0]) {tpcPrintUsage(argv[0], info, stdout); return(1);}
137 if(!outfile[0]) strcpy(outfile, tacfile1);
138 if(overlapMode==OVL_CUTTIME && !(cutTime>0.0)) {fprintf(stderr, "Error: invalid cut time.\n"); return(1);}
139
140 /* In verbose mode print arguments and options */
141 if(verbose>1) {
142 for(ai=0; ai<argc; ai++) printf("%s ", argv[ai]);
143 printf("\n");
144 printf("tacfile1 := %s\n", tacfile1);
145 printf("tacfile2 := %s\n", tacfile2);
146 printf("outfile := %s\n", outfile);
147 printf("overlapMode := %d\n", overlapMode);
148 if(overlapMode==OVL_CUTTIME) printf("cutTime := %g\n", cutTime);
149 printf("forceMode := %d\n", forceMode);
150 fflush(stdout);
151 }
152
153
154 /*
155 * Read the files
156 */
157 TAC tac1, tac2;
158 tacInit(&tac1); tacInit(&tac2);
159 if(verbose>1) printf("reading %s\n", tacfile1);
160 if(tacRead(&tac1, tacfile1, &status)!=TPCERROR_OK) {
161 fprintf(stderr, "Error: %s\n", errorMsg(status.error));
162 tacFree(&tac1); tacFree(&tac2); return(2);
163 }
164 if(verbose>2) {
165 printf("fileformat := %s\n", tacFormattxt(tac1.format));
166 printf("tacNr := %d\n", tac1.tacNr);
167 printf("sampleNr := %d\n", tac1.sampleNr);
168 printf("xunit := %s\n", unitName(tac1.tunit));
169 printf("yunit := %s\n", unitName(tac1.cunit));
170 }
171 if(verbose>1) printf("reading %s\n", tacfile2);
172 if(tacRead(&tac2, tacfile2, &status)!=TPCERROR_OK) {
173 fprintf(stderr, "Error: %s\n", errorMsg(status.error));
174 tacFree(&tac1); tacFree(&tac2); return(3);
175 }
176 if(verbose>2) {
177 printf("fileformat := %s\n", tacFormattxt(tac2.format));
178 printf("tacNr := %d\n", tac2.tacNr);
179 printf("sampleNr := %d\n", tac2.sampleNr);
180 printf("xunit := %s\n", unitName(tac2.tunit));
181 printf("yunit := %s\n", unitName(tac2.cunit));
182 }
183
184 /* Check if the TAC number is different */
185 if(verbose>1) printf("checking data...\n");
186 if(tac1.tacNr!=tac2.tacNr) {
187 fprintf(stderr, "Error: different number of TACs.\n");
188 tacFree(&tac1); tacFree(&tac2); return(4);
189 }
190
191 /* Check that TAC names match, if more than one TAC */
192 if(tac1.tacNr>1 && tacCompareNames(&tac1, &tac2, -1, &status)!=0) {
193 if(forceMode) {
194 fprintf(stderr, "Warning: TAC names do not match.\n");
195 } else {
196 /* Without force mode this is an error, unless sorting helps */
197 tacSortByName(&tac1, NULL);
198 tacSortByName(&tac2, NULL);
199 if(tacCompareNames(&tac1, &tac2, -1, &status)!=0) {
200 fprintf(stderr, "Error: TAC names do not match.\n");
201 tacFree(&tac1); tacFree(&tac2); return(4);
202 }
203 fprintf(stderr, "Note: TACs sorted by name.\n");
204 }
205 }
206
207 /* Delete missing samples */
209 if(tacNotNaNs(&tac1, -1)<1) {
210 fprintf(stderr, "Error: no valid samples in %s\n", tacfile1);
211 tacFree(&tac1); tacFree(&tac2); return(2);
212 }
214 if(tacNotNaNs(&tac2, -1)<1) {
215 fprintf(stderr, "Error: no valid samples in %s\n", tacfile2);
216 tacFree(&tac1); tacFree(&tac2); return(3);
217 }
218
219 /* Sort by sample times */
220 tacSortByTime(&tac1, NULL);
221 tacSortByTime(&tac2, NULL);
222
223 /* Check time units */
224 if(tac1.tunit==UNIT_UNKNOWN || tac2.tunit==UNIT_UNKNOWN) {
225 fprintf(stderr, "Warning: unknown time units.\n");
226 if(forceMode) { // In force mode use what we got
227 if(tac1.tunit!=UNIT_UNKNOWN) tac2.tunit=tac1.tunit;
228 else if(tac2.tunit!=UNIT_UNKNOWN) tac1.tunit=tac2.tunit;
229 }
230 }
231 if(tac2.tunit!=UNIT_UNKNOWN) {
232 /* Try to convert time units to time units in the first file */
233 int ret=tacXUnitConvert(&tac2, tac1.tunit, &status);
234 if(ret!=TPCERROR_OK) {
235 if(verbose>2) fprintf(stderr, "Status: %s\n", errorMsg(status.error));
236 if(forceMode) {
237 fprintf(stderr, "Warning: non-compatible TAC time units.\n");
238 } else {
239 fprintf(stderr, "Error: non-compatible TAC time units.\n");
240 tacFree(&tac1); tacFree(&tac2); return(4);
241 }
242 }
243 }
244 /* If either TAC file only has frame mid times, then both have */
245 if(tac1.isframe==0) tac2.isframe=0;
246 else if(tac2.isframe==0) tac1.isframe=0;
247
248 /* Check concentration units */
249 if(tac1.cunit==UNIT_UNKNOWN || tac2.cunit==UNIT_UNKNOWN) {
250 fprintf(stderr, "Warning: unknown concentration units.\n");
251 if(forceMode) { // In force mode use what we got
252 if(tac1.cunit!=UNIT_UNKNOWN) tac2.cunit=tac1.cunit;
253 else if(tac2.cunit!=UNIT_UNKNOWN) tac1.cunit=tac2.cunit;
254 }
255 }
256 if(tac2.cunit!=UNIT_UNKNOWN) {
257 /* Try to convert concentration units to units in the first file */
258 int ret=tacYUnitConvert(&tac2, tac1.cunit, &status);
259 if(ret!=TPCERROR_OK) {
260 if(verbose>2) fprintf(stderr, "Status: %s\n", errorMsg(status.error));
261 if(forceMode) {
262 fprintf(stderr, "Warning: non-compatible TAC concentration units.\n");
263 } else {
264 fprintf(stderr, "Error: non-compatible TAC concentration units.\n");
265 tacFree(&tac1); tacFree(&tac2); return(4);
266 }
267 }
268 }
269
270 /* If one of TAC files only has frame mid times, then we only use mid times */
271 if(tac1.isframe==0 || tac2.isframe==0) tac1.isframe=tac2.isframe=0;
272 /* Remove weighting unless both have similar weighting */
273 if(tac1.weighting!=tac2.weighting) tac1.weighting=tac2.weighting=WEIGHTING_OFF;
274
275
276 /*
277 * Check if there is overlap in sample times of the two files
278 */
279 if(verbose>1) printf("checking for time overlap\n");
280 int isOverlap=0;
281 double xmin1, xmax1, xmin2, xmax2, xmin, xmax;
282 if(tacXRange(&tac1, &xmin1, &xmax1) || tacXRange(&tac2, &xmin2, &xmax2)) {
283 fprintf(stderr, "Error: invalid sample times.\n");
284 tacFree(&tac1); tacFree(&tac2); return(5);
285 }
286 if(verbose>3) {
287 printf("x range 1 := %g - %g\n", xmin1, xmax1);
288 printf("x range 2 := %g - %g\n", xmin2, xmax2);
289 }
290 if(xmax1>xmin2) isOverlap=1;
291 else if(!tac1.isframe && xmax1>=xmin2) isOverlap=1;
292 if(isOverlap && overlapMode==OVL_ERROR) {
293 fprintf(stderr, "Error: overlapping sample times.\n");
294 tacFree(&tac1); tacFree(&tac2); return(5);
295 }
296 xmin=xmin1; if(xmin2<xmin) xmin=xmin2;
297 xmax=xmax1; if(xmax2>xmax) xmax=xmax2;
298 if(overlapMode==OVL_CUTTIME && (cutTime<xmin || cutTime>xmax)) {
299 fprintf(stderr, "Error: cut time outside of data time range %g - %g\n", xmin, xmax);
300 tacFree(&tac1); tacFree(&tac2); return(5);
301 }
302 if(isOverlap) {
303 int fail=0;
304 if(overlapMode==OVL_USE_FIRST && xmin1>xmin2) fail=1;
305 if(overlapMode==OVL_USE_SECOND && xmin2<=xmin1) fail=2;
306 if(overlapMode==OVL_CUTTIME && (xmin1>cutTime || xmax2<cutTime)) fail=3;
307 if(fail) {
308 fprintf(stderr, "Error: incompatible sample time ranges or wrong file order.\n");
309 tacFree(&tac1); tacFree(&tac2); return(5);
310 }
311 }
312 /* Do not care about overlap if all data is used anyway */
313 if(isOverlap) {
314 if(overlapMode==OVL_USE_BOTH) {
315 isOverlap=0;
316 fprintf(stderr, "Warning: overlap in sample times is ignored.\n");
317 } else {
318 fprintf(stderr, "Warning: overlap in sample times will be removed.\n");
319 }
320 }
321
322 /* Set cut time based on TAC preference, if that is given */
323// if(overlapMode==OVL_USE_FIRST) cutTime=xmax1;
324// if(overlapMode==OVL_USE_SECOND) cutTime=xmin2;
325 if(overlapMode==OVL_USE_FIRST) {cutTime=xmax1; if(xmax1==xmin2) cutTime+=1.0E-12;}
326 if(overlapMode==OVL_USE_SECOND) {cutTime=xmin2; if(xmax1==xmin2) cutTime-=1.0E-12;}
327 if(verbose>4 && isOverlap && (overlapMode==OVL_USE_FIRST || overlapMode==OVL_USE_SECOND))
328 printf("cutTime := %g\n", cutTime);
329
330
331 /* Delete overlapping part from first dataset, if necessary */
332 if(isOverlap && overlapMode!=OVL_USE_FIRST && xmax1>=cutTime) {
333 if(verbose>1) printf("deleting overlap from the first dataset\n");
334 if(tac1.isframe) {
335 for(int i=tac1.sampleNr-1; i>=0; i--)
336 if(tac1.x2[i]>cutTime) tacDeleteSample(&tac1, i);
337 } else {
338 for(int i=tac1.sampleNr-1; i>=0; i--)
339 if(tac1.x[i]>cutTime) tacDeleteSample(&tac1, i);
340 }
341 if(tac1.sampleNr<1 || tacXRange(&tac1, &xmin1, &xmax1)) {
342 fprintf(stderr, "Error: no data from file 1 included.\n");
343 tacFree(&tac1); tacFree(&tac2); return(5);
344 }
345 if(verbose>3) printf("final x range 1 := %g - %g\n", xmin1, xmax1);
346 }
347 /* Delete overlapping part from second dataset, if necessary */
348 if(isOverlap && overlapMode!=OVL_USE_SECOND && xmin2<=cutTime) {
349 if(verbose>1) printf("deleting overlap from the second dataset\n");
350 if(tac1.isframe) {
351 for(int i=tac2.sampleNr-1; i>=0; i--)
352 if(tac2.x1[i]<cutTime) tacDeleteSample(&tac2, i);
353 } else {
354 for(int i=tac2.sampleNr-1; i>=0; i--)
355 if(tac2.x[i]<cutTime) tacDeleteSample(&tac2, i);
356 }
357 if(tac2.sampleNr<1 || tacXRange(&tac2, &xmin2, &xmax2)) {
358 fprintf(stderr, "Error: no data from file 2 included.\n");
359 tacFree(&tac1); tacFree(&tac2); return(5);
360 }
361 if(verbose>3) printf("final x range 2 := %g - %g\n", xmin2, xmax2);
362 }
363
364
365 /*
366 * Add data from second file in to the first one
367 */
368 if(verbose>1) printf("combining data\n");
369 if(tacAllocateMoreSamples(&tac1, tac2.sampleNr)) {
370 fprintf(stderr, "Error: cannot allocate memory.\n");
371 tacFree(&tac1); tacFree(&tac2); return(6);
372 }
373 for(int i=0; i<tac2.sampleNr; i++) {
374 tac1.x[tac1.sampleNr]=tac2.x[i];
375 tac1.x1[tac1.sampleNr]=tac2.x1[i];
376 tac1.x2[tac1.sampleNr]=tac2.x2[i];
377 for(int j=0; j<tac1.tacNr; j++) tac1.c[j].y[tac1.sampleNr]=tac2.c[j].y[i];
378 tac1.w[tac1.sampleNr]=tac2.w[i];
379 tac1.sampleNr++;
380 }
381 /* Sort by sample time */
382 tacSortByTime(&tac1, NULL);
383
384 /* Add interfile type header contents that are stored as comments in file 2 */
385 iftCopyItems(&tac1.h, &tac2.h, 1, 1, 2, NULL);
386 /* Remove duplicate header fields */
387 iftDeleteDuplicateKeys(&tac1.h, NULL);
388
389 /* No need for the second dataset */
390 tacFree(&tac2);
391
392 /*
393 * Save data
394 */
395 if(verbose>1) printf("writing %s\n", outfile);
396 {
397 FILE *fp; fp=fopen(outfile, "w");
398 if(fp==NULL) {
399 fprintf(stderr, "Error: cannot open file for writing (%s)\n", outfile);
400 tacFree(&tac1); return(11);
401 }
402 int ret=tacWrite(&tac1, fp, TAC_FORMAT_UNKNOWN, 1, &status);
403 fclose(fp);
404 if(ret!=TPCERROR_OK) {
405 fprintf(stderr, "Error (%d): %s\n", ret, errorMsg(status.error));
406 tacFree(&tac1); return(12);
407 }
408 if(verbose>=0) {
409 printf("%d samples saved in %s.\n", tac1.sampleNr, outfile);
410 }
411 }
412 tacFree(&tac1);
413
414 return(0);
415}
416/*****************************************************************************/
417
418/*****************************************************************************/
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:632
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