TPCCLIB
Loading...
Searching...
No Matches
tactime.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/*****************************************************************************/
16#include "tpcextensions.h"
17#include "tpcfileutil.h"
18#include "tpcift.h"
19#include "tpccsv.h"
20#include "tpcisotope.h"
21#include "tpctac.h"
22/*****************************************************************************/
23
24/*****************************************************************************/
25static char *info[] = {
26 "Increase or decrease the sample (frame) times in regional or blood/plasma",
27 "TAC files.",
28 "Samples with negative times are not saved by default.",
29 "Time must be given in the same units that are used in the datafile.",
30 " ",
31 "Usage: @P [Options] tacfile [-]time [outputfile]",
32 " ",
33 "Time can be given as positive or negative value directly in the command",
34 "line, or in an ASCII file which contains a line with the time change value",
35 "in the following format: 'time_difference := time'.",
36 "Alternatively, time specified in that file with 'Ta' or 'start_time' is",
37 "subtracted from sample times.",
38 "If 'peak' is given as time, TAC(s) are moved to start at the peak time",
39 "or at start of peak frame.",
40 "If 'gap' is given as time, TAC(s) are moved to remove initial gap time.",
41 " ",
42 "Options:",
43 " -decay",
44 " Physical decay correction is changed with change in sample times;",
45 " without this option, radioactivity values are not changed.",
46 " Note that this option will provide correct result only if time unit",
47 " setting in datafile is correct.",
48 " -i=<isotope>",
49 " Isotope must be known for the decay correction.",
50 " Accepted isotope codes are for example F-18, C-11, and O-15.",
51 " Isotope code can also be specified in input file in format",
52 " '# isotope := C-11'.",
53 " -keepnegat",
54 " Samples with negative sample times are not removed from output.",
55 " -keeptimes",
56 " While correction for physical decay is changed with option -decay,",
57 " sample times will not be changed.",
58 " -nogap",
59 " Possible gap between time zero and first sample is filled.",
60 " -stdoptions", // List standard options like --help, -v, etc
61 " ",
62 "See also: tacframe, tacdecay, injdifft, tacunit, fitdelay, tacmove, taccut",
63 " ",
64 "Keywords: TAC, simulation, late scan, input, time delay, peak",
65 0};
66/*****************************************************************************/
67
68/*****************************************************************************/
69/* Turn on the globbing of the command line, since it is disabled by default in
70 mingw-w64 (_dowildcard=0); in MinGW32 define _CRT_glob instead, if necessary;
71 In Unix&Linux wildcard command line processing is enabled by default. */
72/*
73#undef _CRT_glob
74#define _CRT_glob -1
75*/
76int _dowildcard = -1;
77/*****************************************************************************/
78
79/*****************************************************************************/
83int main(int argc, char **argv)
84{
85 int ai, help=0, version=0, verbose=1;
87 int change_decay=0;
88 int keep_negat=0;
89 int keep_times=0;
90 int fill_gap=0;
91 char tacfile1[FILENAME_MAX], tacfile2[FILENAME_MAX];
92 int peak_start=0; // 1=move TAC to start at peak
93 int gap_start=0; // 1=move TAC so that initial gap is removed
94 int time_sign=1;
95 double time_diff=0.0;
96 int time_diff_unit=UNIT_UNKNOWN;
97
98
99
100 /*
101 * Get arguments
102 */
103 if(argc==1) {tpcPrintUsage(argv[0], info, stderr); return(1);}
104 tacfile1[0]=tacfile2[0]=(char)0;
105 /* Options */
106 for(ai=1; ai<argc; ai++) if(*argv[ai]=='-') {
107 if(tpcProcessStdOptions(argv[ai], &help, &version, &verbose)==0) continue;
108 char *cptr=argv[ai]+1; if(*cptr=='-') cptr++; if(!*cptr) continue;
109 if(strcasecmp(cptr, "DECAY")==0) {
110 change_decay=1; continue;
111 } else if(strncasecmp(cptr, "I=", 2)==0) {
112 cptr+=2;
113 if((isot=isotopeIdentify(cptr))==ISOTOPE_UNKNOWN) {
114 fprintf(stderr, "Error: invalid isotope '%s'.\n", cptr);
115 return(1);
116 }
117 continue;
118 } else if(strncasecmp(cptr, "KEEPNEGATIVE", 5)==0) {
119 keep_negat=1; continue;
120 } else if(strncasecmp(cptr, "KEEPTIMES", 5)==0) {
121 keep_times=1; continue;
122 } else if(strncasecmp(cptr, "NOGAP", 5)==0) {
123 fill_gap=1; continue;
124 }
125 fprintf(stderr, "Error: invalid option '%s'.\n", argv[ai]);
126 return(1);
127 } else break; // tac name argument may start with '-'
128
129 TPCSTATUS status; statusInit(&status);
130 statusSet(&status, __func__, __FILE__, __LINE__, TPCERROR_OK);
131 status.verbose=verbose-3;
132
133 /* Print help or version? */
134 if(help==2) {tpcHtmlUsage(argv[0], info, ""); return(0);}
135 if(help) {tpcPrintUsage(argv[0], info, stdout); return(0);}
136 if(version) {tpcPrintBuild(argv[0], stdout); return(0);}
137
138 /* The first argument (non-option) is the file name */
139 if(ai<argc) {strlcpy(tacfile1, argv[ai], FILENAME_MAX); ai++;}
140 else {fprintf(stderr, "Error: missing file name.\n"); return(1);}
141 if(!fileExist(tacfile1)) {
142 fprintf(stderr, "Error: file '%s' does not exist.\n", tacfile1);
143 return(1);
144 }
145
146 /* The second argument is the time difference */
147 if(ai<argc) {
148 if(verbose>3) printf("argument for time difference: '%s'\n", argv[ai]);
149 /* maybe user wants to move TAC to start from the peak? */
150 if(!strcasecmp("PEAK", argv[ai]) || !strcasecmp("-PEAK", argv[ai])) {
151 peak_start=1; keep_times=0;
152 } else if(!strcasecmp("GAP", argv[ai])) {
153 gap_start=1;
154 } else if(fileExist(argv[ai])) { /* maybe time is given in a file? */
155 IFT ift; iftInit(&ift);
156 FILE *fp=fopen(argv[ai], "r");
157 if(fp==NULL) {
158 fprintf(stderr, "Error: cannot open file %s\n", argv[ai]);
159 return(2);
160 }
161 int ret=iftRead(&ift, fp, 1, 1, &status); fclose(fp);
162 if(ret!=TPCERROR_OK) {
163 fprintf(stderr, "Error: invalid contents in %s\n", argv[ai]);
164 iftFree(&ift); return(2);
165 }
166 /* Find the specific key string */
167 int i=iftFindKey(&ift, "scan_start_time_difference", 0); if(i>=0) time_sign=+1;
168 if(i<0) {i=iftFindKey(&ift, "time_difference", 0); if(i>=0) time_sign=+1;}
169 if(i<0) {i=iftFindKey(&ift, "Ta", 0); if(i>=0) time_sign=-1;}
170 if(i<0) {i=iftFindKey(&ift, "start_time", 0); if(i>=0) time_sign=-1;}
171 if(i<0) {
172 fprintf(stderr, "Error: keyword for time difference not found in '%s'.\n", argv[ai]);
173 iftFree(&ift); return(2);
174 }
175 /* Read the time and its unit */
176 if(iftGetDoubleWithUnit(&ift, i, &time_diff, &time_diff_unit)!=0) {
177 fprintf(stderr, "Error: invalid format for time in '%s'.\n", argv[ai]);
178 iftFree(&ift); return(2);
179 }
180 time_diff*=time_sign;
181 iftFree(&ift);
182 } else { /* then try to read it directly as a value */
183 if(atofCheck(argv[ai], &time_diff)!=0) {
184 fprintf(stderr, "Error: invalid argument for time: '%s'.\n", argv[ai]);
185 return(1);
186 }
187 }
188 if(verbose>7) printf("time_diff := %g\n", time_diff);
189 if(time_diff==0.0 && !peak_start) {
190 fprintf(stderr, "Warning: time change is zero.\n"); fflush(stderr);
191 }
192 ai++;
193 } else {
194 fprintf(stderr, "Error: missing time.\n");
195 return(1);
196 }
197
198 /* Third argument, if it exists, is the output file name */
199 if(ai<argc) {strlcpy(tacfile2, argv[ai], FILENAME_MAX); ai++;}
200 else {strcpy(tacfile2, tacfile1);}
201
202 /* check that there are no extra arguments */
203 if(ai<argc) {fprintf(stderr, "Error: extra command-line argument.\n"); return(1);}
204
205
206 /* Check options */
207 if(keep_times==1 && change_decay==0) {
208 fprintf(stderr, "Error: change in neither sample times or decay correction were requested.\n");
209 return(1);
210 }
211 if(keep_times==1) keep_negat=1;
212
213 /* In verbose mode print arguments and options */
214 if(verbose>1) {
215 printf("tacfile1 := %s\n", tacfile1);
216 printf("tacfile2 := %s\n", tacfile2);
217 printf("keep_times := %d\n", keep_times);
218 printf("keep_negat := %d\n", keep_negat);
219 printf("fill_gap := %d\n", fill_gap);
220 printf("change_decay := %d\n", change_decay);
221 if(isot!=ISOTOPE_UNKNOWN) printf("isotope := %s\n", isotopeName(isot));
222 printf("peak_start := %d\n", peak_start);
223 printf("gap_start := %d\n", gap_start);
224 if(time_diff!=0.0) {
225 printf("time_diff := %g\n", time_diff);
226 if(time_diff_unit!=UNIT_UNKNOWN) printf("time_diff_unit := %s\n", unitName(time_diff_unit));
227 }
228 fflush(stdout);
229 }
230
231
232 /*
233 * Read the file
234 */
235 if(verbose>1) printf("reading %s\n", tacfile1);
236 TAC tac; tacInit(&tac);
237 if(tacRead(&tac, tacfile1, &status)!=TPCERROR_OK) {
238 fprintf(stderr, "Error: %s\n", errorMsg(status.error));
239 tacFree(&tac); return(3);
240 }
241 if(verbose>1) {
242 printf("fileformat := %s\n", tacFormattxt(tac.format));
243 printf("tacNr := %d\n", tac.tacNr);
244 printf("sampleNr := %d\n", tac.sampleNr);
245 if(tac.isframe) printf("frames := yes\n"); else printf("frames := no\n");
246 printf("xunit := %s\n", unitName(tac.tunit));
247 printf("yunit := %s\n", unitName(tac.cunit));
248 fflush(stdout);
249 }
250 if(tacSortByTime(&tac, &status)!=TPCERROR_OK) {
251 fprintf(stderr, "Error: %s\n", errorMsg(status.error));
252 tacFree(&tac); return(3);
253 }
254
255 /* Get and check TAC data range */
256 {
257 double xmin, xmax;
258 if(tacXRange(&tac, &xmin, &xmax)) {
259 fprintf(stderr, "Error: invalid sample range.\n");
260 tacFree(&tac); return(3);
261 }
262 if(verbose>1) printf("x_range: %g - %g\n", xmin, xmax);
263 if(!(xmax>xmin)) {fprintf(stderr, "Warning: check the sample times.\n"); fflush(stderr);}
264 /* If initial gap is to be removed, then set the time difference */
265 if(gap_start==1) {
266 time_diff=-xmin;
267 if(verbose>1) printf("gap_time_diff := %g\n", time_diff);
268 }
269 }
270 int ymax_index;
271 {
272 double ymin, ymax;
273 if(tacYRange(&tac, -1, &ymin, &ymax, NULL, &ymax_index, NULL, NULL)) {
274 fprintf(stderr, "Error: invalid concentration range.\n");
275 tacFree(&tac); return(3);
276 }
277 if(verbose>1) printf("y_range: %g - %g\n", ymin, ymax);
278 if(!(ymax>ymin)) {fprintf(stderr, "Warning: check the data.\n"); fflush(stderr);}
279 }
280 /* Set time_diff based on the peak */
281 if(peak_start) {
282 if(tac.isframe) time_diff=-tac.x1[ymax_index]; else time_diff=-tac.x[ymax_index];
283 time_diff_unit=tac.tunit;
284 if(verbose>1) {
285 printf("peak_index := %d\n", ymax_index);
286 printf("time_diff := %g\n", time_diff);
287 if(time_diff_unit!=UNIT_UNKNOWN) printf("time_diff_unit := %s\n", unitName(time_diff_unit));
288 }
289 }
290
291
292 /* If datafile contains valid isotope, then check that it is the same as given by user */
293 {
294 isotope fisot=tacGetIsotope(&tac);
295 if(verbose>3) {printf("tac.isotope := %s\n", isotopeName(fisot)); fflush(stdout);}
296 if(isot==ISOTOPE_UNKNOWN && fisot!=ISOTOPE_UNKNOWN) {
297 isot=fisot;
298 if(verbose>1) printf("isotope := %s\n", isotopeName(isot));
299 } else if(isot!=ISOTOPE_UNKNOWN && fisot==ISOTOPE_UNKNOWN) {
300 fisot=isot;
301 tacSetIsotope(&tac, fisot);
302 } else if(isot!=ISOTOPE_UNKNOWN && fisot!=ISOTOPE_UNKNOWN && isot!=fisot) {
303 fprintf(stderr, "Error: different isotope in %s\n", tacfile1);
304 tacFree(&tac); return(2);
305 }
306 }
307
308 /* Check that we now have the half life, if decay needs to be changed */
309 if(change_decay && isot==ISOTOPE_UNKNOWN) {
310 fprintf(stderr, "Error: isotope not specified.\n");
311 tacFree(&tac); return(1);
312 }
313
314 /* Check that we know the time unit, if decay needs to be changed */
315 if(change_decay && !unitIsTime(tac.tunit)) {
316 fprintf(stderr, "Error: sample time unit unknown.\n");
317 tacFree(&tac); return(1);
318 }
319
320 /* Convert time difference units to the same as in TAC file */
321 if(unitIsTime(time_diff_unit) && tac.tunit!=UNIT_UNKNOWN) {
322 double f=unitConversionFactor(time_diff_unit, tac.tunit);
323 if(isnan(f)) {
324 fprintf(stderr, "Error: incompatible time units.\n");
325 tacFree(&tac); return(2);
326 }
327 time_diff*=f; time_diff_unit=tac.tunit;
328 }
329 if(verbose>1) printf("final_time_diff := %g %s\n", time_diff, unitName(tac.tunit));
330
331
332
333 /*
334 * Change the sample times
335 */
336 if(keep_times==0) {
337 for(int i=0; i<tac.sampleNr; i++) {
338 tac.x[i]+=time_diff; tac.x1[i]+=time_diff; tac.x2[i]+=time_diff;
339 }
340 }
341 /* Remove samples with negative times */
342 if(keep_negat==0) {
343 int i=0;
344 while(tac.sampleNr>0 && tac.x[i]<0.0) tacDeleteSample(&tac, i);
345 if(tac.sampleNr<=0) {
346 fprintf(stderr, "Error: No positive times left.\n");
347 tacFree(&tac); return(5);
348 }
349 }
350 /* Make sure that there is no initial time gap, if requested */
351 if(fill_gap!=0) {
352 if(verbose>1) printf("filling initial time gap\n");
353 if(tacAddZeroSample(&tac, &status)!=TPCERROR_OK) {
354 fprintf(stderr, "Error: cannot fill time gap.\n");
355 tacFree(&tac); return(5);
356 }
357 }
358
359
360 /*
361 * Change decay correction for the time difference, if requested
362 */
363 if(change_decay && time_diff!=0.0) {
364 if(verbose>3) printf("decay correcting for time %g\n", time_diff);
365 /* Calculate decay correction factor */
366 double tdiff=fabs(time_diff)*unitConversionFactor(tac.tunit, UNIT_MIN);
367 if(verbose>5) printf(" tdiff := %g\n", tdiff);
368 double dcf=decayCorrectionFactorFromIsotope(isot, tdiff, 0.0);
369 if(time_diff<0.0) dcf=1.0/dcf;
370 if(isnan(dcf)) {
371 fprintf(stderr, "Error: cannot calculate decay correction factor.\n");
372 tacFree(&tac); return(1);
373 }
374 if(verbose>1) printf("decay_correction_factor := %g\n", dcf);
375 /* Correct all TACs */
376 for(int j=0; j<tac.tacNr; j++) for(int i=0; i<tac.sampleNr; i++) tac.c[j].y[i]*=dcf;
377 /* Opposite correction for weights, if available */
378 if(tacIsWeighted(&tac)) {
379 for(int i=0; i<tac.sampleNr; i++) tac.w[i]/=dcf;
380 tacWeightNorm(&tac, NULL);
381 }
382 }
383
384
385 /*
386 * If sample times were not changed, and datafile contains injection time,
387 * then change the injection time accordingly
388 */
389 if(keep_times!=0 && tacGetHeaderInjectiontime(&tac.h, NULL, NULL)==TPCERROR_OK) {
390 char buf[20];
391 tacGetHeaderInjectiontime(&tac.h, buf, &status);
392 if(verbose>2) printf("injection_time: %s\n", buf);
393 double tdiff=-time_diff*unitConversionFactor(tac.tunit, UNIT_SEC);
394 if(verbose>5) printf(" injection_time_tdiff := %g s\n", tdiff);
395 if(strDateTimeAdd((int)round(tdiff), buf) || tacSetHeaderInjectiontime(&tac.h, buf)!=TPCERROR_OK)
396 {
397 fprintf(stderr, "Error: cannot correct injection time.\n");
398 tacFree(&tac); return(1);
399 }
400 if(verbose>0) printf("updated_injection_time := %s\n", buf);
401 }
402
403
404
405 /*
406 * Save the modified data
407 */
408 if(verbose>1) printf("saving modified data in %s\n", tacfile2);
409 {
410 FILE *fp; fp=fopen(tacfile2, "w");
411 if(fp==NULL) {
412 fprintf(stderr, "Error: cannot open file for writing\n");
413 tacFree(&tac); return(11);
414 }
415 int ret=tacWrite(&tac, fp, TAC_FORMAT_UNKNOWN, 1, &status);
416 fclose(fp);
417 if(ret!=TPCERROR_OK) {
418 fprintf(stderr, "Error: %s\n", errorMsg(status.error));
419 tacFree(&tac); return(12);
420 }
421
422 /* Tell user what we did */
423 if(verbose>=0) {
424 if(keep_times==0) fprintf(stdout, "Sample times changed by %g", time_diff);
425 else fprintf(stdout, "Injection time changed by %g", -time_diff);
426 if(change_decay) fprintf(stdout, " and values with decay of %s", isotopeName(isot));
427 fprintf(stdout, " in %s\n", tacfile2);
428 fflush(stdout);
429 }
430 }
431 tacFree(&tac);
432
433 return(0);
434}
435/*****************************************************************************/
436
437/*****************************************************************************/
int strDateTimeAdd(int s, char *dt)
Definition datetime.c:537
double decayCorrectionFactorFromIsotope(int isotope, double starttime, double duration)
Definition decay.c:107
int atofCheck(const char *s, double *v)
Definition decpoint.c:94
int fileExist(const char *filename)
Definition filexist.c:17
void iftFree(IFT *ift)
Definition ift.c:37
void iftInit(IFT *ift)
Definition ift.c:21
int iftFindKey(IFT *ift, const char *key, int start_index)
Definition iftfind.c:30
int iftGetDoubleWithUnit(IFT *ift, int index, double *v, int *unit)
Definition iftget.c:129
int iftRead(IFT *ift, FILE *fp, int is_key_required, int is_comment_accepted, TPCSTATUS *status)
Definition iftio.c:130
char * isotopeName(int isotope_code)
Definition isotope.c:101
int isotopeIdentify(const char *isotope)
Definition isotope.c:145
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
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 tacGetIsotope(TAC *tac)
Definition tacdc.c:25
void tacSetIsotope(TAC *tac, int isotope)
Definition tacdc.c:41
int tacGetHeaderInjectiontime(IFT *h, char *s, TPCSTATUS *status)
Definition tacift.c:462
int tacSetHeaderInjectiontime(IFT *h, const char *s)
Definition tacift.c:515
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 tacSortByTime(TAC *d, TPCSTATUS *status)
Definition tacorder.c:74
int tacIsWeighted(TAC *tac)
Definition tacw.c:24
int tacWeightNorm(TAC *tac, TPCSTATUS *status)
Definition tacw.c:237
int tacAddZeroSample(TAC *d, TPCSTATUS *status)
Add an initial sample to TAC(s) with zero time and concentration.
Definition tacx.c:366
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
int tacYRange(TAC *d, int i, double *ymin, double *ymax, int *smin, int *smax, int *imin, int *imax)
Get the range of y values (concentrations) in TAC struct.
Definition tacy.c:26
Header file for library libtpccsv.
Header file for library libtpcextensions.
@ UNIT_MIN
minutes
@ UNIT_UNKNOWN
Unknown unit.
@ UNIT_SEC
seconds
@ TPCERROR_OK
No error.
double unitConversionFactor(const int u1, const int u2)
Definition units.c:487
char * unitName(int unit_code)
Definition units.c:143
int unitIsTime(int u)
Definition units.c:359
Header file for libtpcfileutil.
Header file for library libtpcift.
Header file for library libtpcisotope.
isotope
Definition tpcisotope.h:50
@ ISOTOPE_UNKNOWN
Unknown.
Definition tpcisotope.h:51
Header file for library libtpctac.
@ TAC_FORMAT_UNKNOWN
Unknown format.
Definition tpctac.h:28