TPCCLIB
Loading...
Searching...
No Matches
tacx.c File Reference

Working with x values (sample times) in TAC structure. More...

#include "tpcclibConfig.h"
#include "tpcift.h"
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <string.h>
#include "tpctac.h"

Go to the source code of this file.

Functions

int tacXCopy (TAC *tac1, TAC *tac2, int i1, int i2)
 
int tacCorrectFrameOverlap (TAC *d, TPCSTATUS *status)
 Correct PET frame start and end times if frames are slightly overlapping or have small gaps in between.
 
int tacXRange (TAC *d, double *xmin, double *xmax)
 Get the range of x values (times) in TAC structure.
 
int tacSampleXRange (TAC *d, double *xmin, double *xmax)
 Get the range of x values (times) in TAC structure.
 
int tacMinX (TAC *d)
 Get the minimum x value in TAC structure.
 
int tacIsX (TAC *d)
 Verify if TAC structure contains reasonable x values (times).
 
int tacXMatch (TAC *d1, TAC *d2, const int verbose)
 Check whether sample (frame) times are the same (or very close to) in two TAC structures.
 
int tacAddZeroSample (TAC *d, TPCSTATUS *status)
 Add an initial sample to TAC(s) with zero time and concentration.
 
int tacDeleteSample (TAC *d, int i)
 Delete a certain sample (time frame) from TAC structure.
 
int tacDeleteMissingSamples (TAC *d)
 Delete those samples (time frames) from TAC structure, which contain only missing y values, and/or x is NaN.
 
int tacExtractRange (TAC *d1, TAC *d2, double startT, double endT)
 Extract the specified time (x) range from TAC structure.
 
int tacExtractSamples (TAC *d1, TAC *d2, int si, int ei)
 Extract the specified sample range from TAC structure.
 
int tacCheckX1X2X (TAC *d)
 Check that sample (time frame) x, x1, and x2 values are reasonably set when compared to each other in TAC structure.
 
int tacSetX (TAC *d, TPCSTATUS *status)
 Set TAC x values based on x1 and x2 values, or guess x1 and x2 values based on x values.
 
int tacGetSampleInterval (TAC *d, double ilimit, double *minfdur, double *maxfdur)
 Get the shortest and longest sampling intervals or frame lengths in TAC structure.
 
int tacToBars (TAC *tac1, TAC *tac2)
 Transform TAC data with frame start and end times into suitable form for plotting with frames as bars.
 
int tacFramesToSteps (TAC *inp, TAC *out, TPCSTATUS *status)
 Transform TAC with frames into TAC with frames represented with stepwise changing dot-to-dot data.
 
int tacIsXContiguous (TAC *d)
 Check that PET TAC frame times are contiguous, without even tiny overlap or gaps in between.
 
int tacSetXContiguous (TAC *d)
 Set PET TAC frame times contiguous, without even tiny overlap or gaps in between.
 

Detailed Description

Working with x values (sample times) in TAC structure.

Definition in file tacx.c.

Function Documentation

◆ tacAddZeroSample()

int tacAddZeroSample ( TAC * d,
TPCSTATUS * status )

Add an initial sample to TAC(s) with zero time and concentration.

If the sample time of the first sample is zero (or smaller) already, then nothing is done. Sort by sample time before calling this.

See also
tacSortByTime, tacAllocateMoreSamples, tacIsX, tacCheckX1X2X, tacMinX, tacInterpolateToEqualLengthFrames
Author
Vesa Oikonen
Returns
Returns TPCERROR status (0 if ok).
Parameters
dPointer to the TAC structure.
statusPointer to status data; enter NULL if not needed.

Definition at line 366 of file tacx.c.

371 {
372 int verbose=0; if(status!=NULL) verbose=status->verbose;
373 if(verbose>0) printf("%s()\n", __func__);
374 if(d==NULL || d->_sampleNr<1 || d->_tacNr<1) {
375 statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_FAIL);
376 return TPCERROR_FAIL;
377 }
378
379 /* Check if we already have the zero sample */
380 if(d->sampleNr>0) {
381 if((d->isframe && d->x1[0]<=0.0) || (!d->isframe && d->x[0]<=0.0)) {
382 statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_OK);
383 return TPCERROR_OK;
384 }
385 }
386
387 /* If we have no samples at all, but space for samples, then just make one */
388 if(d->sampleNr==0 && d->_sampleNr>0) {
389 d->x[0]=d->x1[0]=d->x2[0]=0.0; d->sampleNr=1;
390 for(int j=0; j<d->tacNr; j++) d->c[j].y[0]=0.0;
391 statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_OK);
392 return TPCERROR_OK;
393 }
394
395 /* Add space for one more sample */
396 int ret;
397 ret=tacAllocateMoreSamples(d, 1);
398 if(ret!=TPCERROR_OK) {statusSet(status, __func__, __FILE__, __LINE__, ret); return ret;}
399
400 /* Move all data to make room for the zero sample */
401 for(int fi=d->sampleNr; fi>0; fi--) {
402 d->x[fi]=d->x[fi-1]; d->x1[fi]=d->x1[fi-1]; d->x2[fi]=d->x2[fi-1];
403 for(int ri=0; ri<d->tacNr; ri++) d->c[ri].y[fi]=d->c[ri].y[fi-1];
404 d->w[fi]=d->w[fi-1];
405 } // previous sample
406 d->sampleNr++;
407 /* ... and then add the zero sample */
408 for(int ri=0; ri<d->tacNr; ri++) d->c[ri].y[0]=0.0;
409 if(!d->isframe) {
410 d->x[0]=d->x1[0]=d->x2[0]=0.0;
411 } else {
412 d->x1[0]=0.0; d->x2[0]=d->x1[1]; d->x[0]=0.5*(d->x1[0]+d->x2[0]);
413 }
414
415 statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_OK);
416 return TPCERROR_OK;
417}
void statusSet(TPCSTATUS *s, const char *func, const char *srcfile, int srcline, tpcerror error)
Definition statusmsg.c:142
double * y
Definition tpctac.h:75
double * x
Definition tpctac.h:97
int _sampleNr
Definition tpctac.h:121
int sampleNr
Definition tpctac.h:89
double * w
Definition tpctac.h:111
int isframe
Definition tpctac.h:95
TACC * c
Definition tpctac.h:117
double * x2
Definition tpctac.h:101
int _tacNr
Definition tpctac.h:119
double * x1
Definition tpctac.h:99
int tacNr
Definition tpctac.h:91
int verbose
Verbose level, used by statusPrint() etc.
int tacAllocateMoreSamples(TAC *tac, int addNr)
Allocate memory for more samples in TAC data.
Definition tac.c:435
@ TPCERROR_FAIL
General error.
@ TPCERROR_OK
No error.

Referenced by tacInput2sim().

◆ tacCheckX1X2X()

int tacCheckX1X2X ( TAC * d)

Check that sample (time frame) x, x1, and x2 values are reasonably set when compared to each other in TAC structure.

See also
tacSortByTime, tacIsX, tacAddZeroSample, tacXCopy, tacSetX, tacXNaNs, tacGetSampleInterval, tacIsXContiguous
Author
Vesa Oikonen
Returns
Returns 1 if reasonable, 0 if not.
Parameters
dPointer to the TAC structure. TAC samples must be sorted. TAC isframe setting has no effect here.

Definition at line 621 of file tacx.c.

625 {
626 if(d==NULL || d->sampleNr<1) return(0);
627 int i, n;
628 double x1, x2, x, dx;
629
630 for(i=n=0; i<d->sampleNr; i++) {
631 x1=d->x1[i]; x2=d->x2[i]; dx=x2-x1; x=0.5*(x1+x2);
632 /* if not similar, or both missing (NaN), that is considered as non-match */
633 if(!doubleMatch(x, d->x[i], 0.15*dx)) {n++; break;}
634 /* check that there is not much overlap with previous sample */
635 if(i>0 && (x1+0.15*dx)<d->x2[i-1]) {n++; break;}
636 /* check that there is not much gap on both sides */
637 if(i>0 && i<d->sampleNr-1) {
638 if(x1-0.15*dx>d->x2[i-1] && x2+0.15*dx<d->x1[i+1]) {n++; break;}
639 }
640 }
641 if(n>0) return(0); else return(1);
642}
int doubleMatch(const double v1, const double v2, const double lim)
Definition doubleutil.c:27

Referenced by tacSetX().

◆ tacCorrectFrameOverlap()

int tacCorrectFrameOverlap ( TAC * d,
TPCSTATUS * status )

Correct PET frame start and end times if frames are slightly overlapping or have small gaps in between.

Large gap is not corrected and it does not lead to an error. If TAC file does not contain frame start and end times, but just mid time point, then this function does nothing but check that samples are in correct order. Data must be sorted for increasing sample time before calling this function, otherwise an error is returned.

See also
tacIsX, tacXRange, tacVerifyTimeOrder, tacSortByTime, tacXCopy, tacMultipleSamples, tacFramesToSteps
Returns
If overlap is considerable (>20%), or another error is encountered, function returns a non-zero enum tpcerror value. Otherwise 0 (TPCERROR_OK) is returned.
Author
Vesa Oikonen
Test
Add tests.
Parameters
dPointer to TAC data which is sorted by increasing sample time.
statusPointer to status data; enter NULL if not needed.

Definition at line 65 of file tacx.c.

70 {
71 int verbose=0; if(status!=NULL) verbose=status->verbose;
72 if(verbose>0) printf("%s()\n", __func__);
73 if(d==NULL) {
74 statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_FAIL);
75 return TPCERROR_FAIL;
76 }
77 /* If no frame start and end times, then just check the order */
78 if(d->isframe==0) {
79 if(tacVerifyTimeOrder(d, status)) {
80 if(verbose>0) fprintf(stderr, "Error: sample times are not in order.\n");
81 return(status->error);
82 }
83 statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_OK);
84 return TPCERROR_OK;
85 }
86 /* Check each frame */
87 double overlap, flen1, flen2, overlap_limit=0.0;
88 for(int fi=0; fi<d->sampleNr-1; fi++) {
89 overlap=d->x2[fi]-d->x1[fi+1];
90 if(overlap==0.0) continue; // no gap or overlap
91 /* Calculate the frame length of current frame and the next frame */
92 flen1=d->x2[fi]-d->x1[fi]; flen2=d->x2[fi+1]-d->x1[fi+1];
93 if(flen1<0.0 || flen2<0.0) return(1);
94 /* Set the limit */
95 if(flen1<flen2) overlap_limit=0.2*flen1; else overlap_limit=0.2*flen2;
96 /* Check if gap or overlap is too large to be fixed automatically */
97 if(overlap<-overlap_limit) continue; // gap is too large, then do nothing
98 if(overlap>overlap_limit) { // overlap is too large: error
99 statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_OVERLAPPING_DATA);
101 }
102 /* Correct the small gap/overlap by making frame durations more similar */
103 if(overlap>0.0) { // overlap
104 if(flen1>flen2) d->x2[fi]=d->x1[fi+1]; else d->x1[fi+1]=d->x2[fi];
105 } else { // gap
106 if(flen1>flen2) d->x1[fi+1]=d->x2[fi]; else d->x2[fi]=d->x1[fi+1];
107 }
108 }
109
110 statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_OK);
111 return TPCERROR_OK;
112}
tpcerror error
Error code.
int tacVerifyTimeOrder(TAC *d, TPCSTATUS *status)
Definition tacorder.c:25
@ TPCERROR_OVERLAPPING_DATA
Overlapping data.

Referenced by tacReadModelingData().

◆ tacDeleteMissingSamples()

int tacDeleteMissingSamples ( TAC * d)

Delete those samples (time frames) from TAC structure, which contain only missing y values, and/or x is NaN.

See also
tacDeleteSample, tacExtractRange, tacIsX, tacXNaNs, tacFixNaNs, tacIsXContiguous
Returns
Returns TPCERROR status (0 if ok).
Parameters
dPointer to the TAC structure.

Definition at line 450 of file tacx.c.

453 {
454 if(d==NULL) return TPCERROR_FAIL;
455 int fi, ri, n, delthat=0;
456 fi=d->sampleNr-1;
457 while(fi>=0) {
458 delthat=0;
459 /* check the x value(s) for this sample */
460 if(d->isframe) {
461 if(!isfinite(d->x1[fi]) || !isfinite(d->x2[fi])) delthat=1;
462 } else {
463 if(!isfinite(d->x[fi])) delthat=1;
464 }
465 /* check the y value(s) for this sample */
466 for(ri=n=0; ri<d->tacNr; ri++) if(isfinite(d->c[ri].y[fi])) n++;
467 if(n==0) delthat=1;
468 if(delthat!=0) {
469 /* delete sample */
470 tacDeleteSample(d, fi);
471 }
472 /* go to previous sample */
473 fi--;
474 }
475 return TPCERROR_OK;
476}
int tacDeleteSample(TAC *d, int i)
Delete a certain sample (time frame) from TAC structure.
Definition tacx.c:426

Referenced by tacMultipleSamples().

◆ tacDeleteSample()

int tacDeleteSample ( TAC * d,
int i )

Delete a certain sample (time frame) from TAC structure.

See also
tacAllocateMoreSamples, tacExtractRange, tacDeleteMissingSamples, tacMultipleSamples
Author
Vesa Oikonen
Returns
Returns TPCERROR status (0 if ok).
Parameters
dPointer to the TAC structure.
iIndex of the sample to delete, [0..sampleNr-1].

Definition at line 426 of file tacx.c.

431 {
432 if(d==NULL || i<0 || i>=d->sampleNr) return TPCERROR_FAIL;
433 int fi, ri;
434 for(fi=i+1; fi<d->sampleNr; fi++) {
435 d->x[fi-1]=d->x[fi]; d->x1[fi-1]=d->x1[fi]; d->x2[fi-1]=d->x2[fi];
436 for(ri=0; ri<d->tacNr; ri++) d->c[ri].y[fi-1]=d->c[ri].y[fi];
437 d->w[fi-1]=d->w[fi];
438 }
439 d->sampleNr--;
440 return TPCERROR_OK;
441}

Referenced by tacDeleteMissingSamples(), and tacMultipleSamples().

◆ tacExtractRange()

int tacExtractRange ( TAC * d1,
TAC * d2,
double startT,
double endT )

Extract the specified time (x) range from TAC structure.

See also
tacDeleteSample, tacDeleteMissingSamples, tacExtractSamples, tacCheckX1X2X, tacGetSampleInterval
Author
Vesa Oikonen
Returns
Returns TPCERROR status (0 if ok).
Parameters
d1Pointer to the source TAC structure. Data must be sorted by ascending x.
d2Pointer to the target TAC structure; any previous contents are deleted. The source TAC pointer can be given for in-place extraction.
See also
tacInit, tacFree
Parameters
startTStart time of data that is preserved (in same units as in TAC)
endTEnd time of data that is preserved (in same units as in TAC)

Definition at line 486 of file tacx.c.

497 {
498 if(d1==NULL || d2==NULL) return TPCERROR_FAIL;
499 if(d1->sampleNr<1) return TPCERROR_NO_DATA;
500 if(endT<startT) return TPCERROR_INVALID_VALUE;
501
502 /* Duplicate source TAC, in case target points to the same data */
503 TAC tac; tacInit(&tac); if(tacDuplicate(d1, &tac)!=TPCERROR_OK) return TPCERROR_FAIL;
504
505 /* Remove any old contents in target structure */
506 tacFree(d2);
507
508 /* Get the range of TAC frame middle times */
509 double beg, end;
510 if(tac.isframe) {
511 beg=0.5*(tac.x1[0]+tac.x2[0]);
512 end=0.5*(tac.x1[tac.sampleNr-1]+tac.x2[tac.sampleNr-1]);
513 } else {
514 beg=tac.x[0]; end=tac.x[tac.sampleNr-1];
515 }
516
517 /* Check whether required range is outside the TAC range */
518 if(startT>=end || endT<beg) {tacFree(&tac); return TPCERROR_NO_DATA;}
519
520 /* If the whole TAC is inside required range, then just copy it */
521 if(startT<=beg && endT>=end) {
522 int ret=tacDuplicate(&tac, d2);
523 tacFree(&tac); return(ret);
524 }
525
526 /* Count how many samples are inside the required range */
527 /* And get the first and last sample indices */
528 int i, n=0, i1=-1, i2=-1;
529 double x;
530 for(i=0; i<tac.sampleNr; i++) {
531 if(tac.isframe) x=0.5*(tac.x1[i]+tac.x2[i]); else x=tac.x[i];
532 if(x<startT) continue;
533 if(x>endT) break;
534 i2=i; if(i1<0) i1=i;
535 n++;
536 }
537 if(n==0 || i1<0 || i2<i1) {tacFree(&tac); return TPCERROR_NO_DATA;}
538
539 /* Allocate memory for the data to be extracted */
540 int ret;
541 ret=tacAllocate(d2, n, tac.tacNr); if(ret!=TPCERROR_OK) {tacFree(&tac); return(ret);}
542 d2->sampleNr=n;
543 d2->tacNr=tac.tacNr;
544
545 /* Copy the contents */
546 ret=tacCopyHdr(&tac, d2); if(ret!=TPCERROR_OK) {tacFree(&tac); return(ret);}
547 for(int j=0, ret=0; j<tac.tacNr && ret==0; j++)
548 ret=tacCopyTacchdr(&tac.c[j], &d2->c[j]);
549 if(ret!=TPCERROR_OK) {tacFree(&tac); return(ret);}
550 d2->isframe=tac.isframe;
551 d2->weighting=tac.weighting;
552 for(i=i1, n=0; i<=i2 && n<d2->sampleNr; i++, n++) {
553 d2->x[n]=tac.x[i]; d2->x1[n]=tac.x1[i]; d2->x2[n]=tac.x2[i];
554 d2->w[n]=tac.w[i];
555 for(int j=0; j<tac.tacNr; j++) d2->c[j].y[n]=tac.c[j].y[i];
556 }
557 tacFree(&tac);
558 return TPCERROR_OK;
559}
Definition tpctac.h:87
weights weighting
Definition tpctac.h:115
void tacFree(TAC *tac)
Definition tac.c:106
int tacDuplicate(TAC *tac1, TAC *tac2)
Make a duplicate of TAC structure.
Definition tac.c:356
int tacAllocate(TAC *tac, int sampleNr, int tacNr)
Definition tac.c:130
void tacInit(TAC *tac)
Definition tac.c:24
int tacCopyTacchdr(TACC *d1, TACC *d2)
Definition tac.c:282
int tacCopyHdr(TAC *tac1, TAC *tac2)
Copy TAC header data from tac1 to tac2.
Definition tac.c:310
@ TPCERROR_INVALID_VALUE
Invalid value.
@ TPCERROR_NO_DATA
File contains no data.

◆ tacExtractSamples()

int tacExtractSamples ( TAC * d1,
TAC * d2,
int si,
int ei )

Extract the specified sample range from TAC structure.

See also
tacExtractRange, tacDeleteSample, tacDeleteMissingSamples, tacInit, tacFree
Author
Vesa Oikonen
Returns
Returns TPCERROR status (0 if ok).
Parameters
d1Pointer to the source TAC structure. Data must be sorted by ascending x.
d2Pointer to the target TAC structure; any previous contents are deleted.
siIndex of first sample to extract.
eiIndex of last sample to extract.

Definition at line 568 of file tacx.c.

577 {
578 if(d1==NULL || d2==NULL) return TPCERROR_FAIL;
579 if(d1->sampleNr<1) return TPCERROR_NO_DATA;
580 if(si<0 || ei<si || si>d1->sampleNr-1) return TPCERROR_INVALID_VALUE;
581 if(ei>d1->sampleNr-1) ei=d1->sampleNr-1;
582
583 /* Remove any old contents in target struct */
584 tacFree(d2);
585
586 /* If the whole TAC is inside required range, then just copy it */
587 if(si==0 && ei==d1->sampleNr-1) return(tacDuplicate(d1, d2));
588
589 /* Count how many samples are inside the required range */
590 int n=1+ei-si; if(n==0) return TPCERROR_NO_DATA;
591
592 /* Allocate memory for the data to be extracted */
593 int ret=tacAllocate(d2, n, d1->tacNr); if(ret!=TPCERROR_OK) return(ret);
594 d2->sampleNr=n;
595 d2->tacNr=d1->tacNr;
596
597 /* Copy the contents */
598 ret=tacCopyHdr(d1, d2); if(ret!=TPCERROR_OK) return(ret);
599 for(int j=0, ret=0; j<d1->tacNr && ret==0; j++) ret=tacCopyTacchdr(&d1->c[j], &d2->c[j]);
600 if(ret!=TPCERROR_OK) return(ret);
601 d2->isframe=d1->isframe;
602 d2->weighting=d1->weighting;
603 for(int i=si, n=0; i<=ei && n<d2->sampleNr; i++, n++) {
604 d2->x[n]=d1->x[i]; d2->x1[n]=d1->x1[i]; d2->x2[n]=d1->x2[i];
605 d2->w[n]=d1->w[i];
606 for(int j=0; j<d1->tacNr; j++) d2->c[j].y[n]=d1->c[j].y[i];
607 }
608
609 return TPCERROR_OK;
610}

◆ tacFramesToSteps()

int tacFramesToSteps ( TAC * inp,
TAC * out,
TPCSTATUS * status )

Transform TAC with frames into TAC with frames represented with stepwise changing dot-to-dot data.

See also
tacInterpolate, tacToBars, tacAUC, tacDelay, tacInterpolateToEqualLengthFrames, liInterpolate
Returns
enum tpcerror (TPCERROR_OK when successful).
Parameters
inpPointer to source TAC structure.
Precondition
Data must be sorted by increasing x. There must be no gaps or overlap between frames.
See also
tacSortByTime, tacSetXContiguous
Parameters
outPointer to the initiated target TAC structure; any previous contents are deleted.
statusPointer to status data; enter NULL if not needed.

Definition at line 942 of file tacx.c.

952 {
953 int verbose=0; if(status!=NULL) verbose=status->verbose;
954 if(verbose>0) printf("%s()\n", __func__);
955 if(inp==NULL || out==NULL) {
956 statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_FAIL);
957 return TPCERROR_FAIL;
958 }
959 /* Check the function input */
960 if(inp->sampleNr<1 || inp->tacNr<1) {
961 statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_NO_DATA);
962 return TPCERROR_NO_DATA;
963 }
964
965 /* Check that input does not have any missing values */
966 if(tacNaNs(inp)>0) {
967 statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_MISSING_DATA);
969 }
970
971 /* Check that there are no gaps or overlap between frames */
972 {
973 //printf(" checking that there are no gaps or overlap\n"); fflush(stdout);
974 int ret=tacIsXContiguous(inp);
975 statusSet(status, __func__, __FILE__, __LINE__, ret);
976 if(ret!=TPCERROR_OK) return(ret);
977 }
978
979 /* First, duplicate the TAC data */
980 //printf(" copying data\n"); fflush(stdout);
981 if(tacDuplicate(inp, out)!=TPCERROR_OK) {
982 statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_FAIL);
983 return TPCERROR_FAIL;
984 }
985
986 /* If frame start and end times are NOT present, then consider that work is done */
987 if(inp->isframe==0) return(TPCERROR_OK);
988
989 /* Add room for more frames */
990 //printf(" allocating memory for output\n"); fflush(stdout);
991 if(tacAllocateMoreSamples(out, 2+2*out->sampleNr)!=0) {
992 tacFree(out);
993 statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_OUT_OF_MEMORY);
995 }
996
997 /* Make separate 'samples' for each frame start and end time */
998 out->isframe=0;
999 out->sampleNr=0;
1000 /* Add zero sample at zero time if data starts later */
1001 if(inp->x1[0]>0.01*(inp->x2[0]-inp->x1[0])) {
1002 if(verbose>10) printf(" add zero sample\n");
1003 out->x[0]=0.0;
1004 for(int ri=0; ri<out->tacNr; ri++) out->c[ri].y[0]=0.0;
1005 out->sampleNr++;
1006 }
1007 /* Add zero sample at the start of the first frame */
1008 if(verbose>10) printf(" add first frame zero sample\n");
1009 out->x[out->sampleNr]=inp->x1[0];
1010 for(int ri=0; ri<out->tacNr; ri++) out->c[ri].y[out->sampleNr]=0.0;
1011 out->sampleNr++;
1012
1013 /* Add frame start and end times */
1014 for(int fi=0; fi<inp->sampleNr; fi++) {
1015 if(verbose>10) printf(" frame %d: %g - %g\n", 1+fi, inp->x1[fi], inp->x2[fi]);
1016 /* add start sample, if any of y values differs from previous one */
1017 out->x[out->sampleNr]=out->x[out->sampleNr-1];
1018// if(fi==0) out->x[out->sampleNr]=inp->x1[fi];
1019// else out->x[out->sampleNr]=out->x[out->sampleNr-1];
1020 int dn=0;
1021 for(int ri=0; ri<out->tacNr; ri++) {
1022 out->c[ri].y[out->sampleNr]=inp->c[ri].y[fi];
1023 if(out->c[ri].y[out->sampleNr]!=out->c[ri].y[out->sampleNr-1]) dn++;
1024 }
1025 if(dn>0) out->sampleNr++;
1026 /* add end sample */
1027 out->x[out->sampleNr]=inp->x2[fi];
1028 for(int ri=0; ri<out->tacNr; ri++)
1029 out->c[ri].y[out->sampleNr]=inp->c[ri].y[fi];
1030 out->sampleNr++;
1031 }
1032
1033 statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_OK);
1034 return(TPCERROR_OK);
1035}
int tacNaNs(TAC *tac)
Definition tacnan.c:71
int tacIsXContiguous(TAC *d)
Check that PET TAC frame times are contiguous, without even tiny overlap or gaps in between.
Definition tacx.c:1130
@ TPCERROR_OUT_OF_MEMORY
Cannot allocate memory.
@ TPCERROR_MISSING_DATA
File contains missing values.

Referenced by tacAUC(), and tacDelay().

◆ tacGetSampleInterval()

int tacGetSampleInterval ( TAC * d,
double ilimit,
double * minfdur,
double * maxfdur )

Get the shortest and longest sampling intervals or frame lengths in TAC structure.

Data is not modified. Data must be sorted by increasing x.

See also
tacXRange, tacIsX, tacCheckX1X2X, tacInterpolateToEqualLengthFrames
Author
Vesa Oikonen
Returns
Returns <>0 in case of failure.
Parameters
dPointer to TAC structure.
ilimitInterval limit: smaller sampling intervals of frame lengths are not accepted; use this to prevent 0 interval in case of step functions or zero length frames; enter NaN if not applied.
minfdurPointer to variable for minimum interval (NULL if not needed).
maxfdurPointer to variable for maximum interval (NULL if not needed).

Definition at line 832 of file tacx.c.

843 {
844 if(minfdur!=NULL) *minfdur=nan("");
845 if(maxfdur!=NULL) *maxfdur=nan("");
846 /* Check the data */
847 if(d==NULL || d->sampleNr<1) return(1);
848 double mi=nan(""), ma=nan("");
849 if(d->isframe) {
850 for(int i=0; i<d->sampleNr; i++) {
851 double f=d->x2[i]-d->x1[i];
852 if(!isnan(ilimit) && !(f>=ilimit)) continue;
853 if(isnan(mi) || f<mi) mi=f;
854 if(isnan(ma) || f>ma) ma=f;
855 }
856 } else {
857 for(int i=1; i<d->sampleNr; i++) {
858 double f=d->x[i]-d->x[i-1];
859 if(!isnan(ilimit) && !(f>=ilimit)) continue;
860 if(isnan(mi) || f<mi) mi=f;
861 if(isnan(ma) || f>ma) ma=f;
862 }
863 if(isnan(mi) || isnan(ma)) { // as the last resort use the time of first sample
864 double f=d->x[0];
865 if(f>0.0 && !isnan(ilimit) && f>=ilimit) {
866 if(isnan(mi)) mi=f;
867 if(isnan(ma)) ma=f;
868 }
869 }
870 }
871 if(minfdur!=NULL) *minfdur=mi;
872 if(maxfdur!=NULL) *maxfdur=ma;
873
874 if(minfdur!=NULL && isnan(mi)) return(2);
875 if(maxfdur!=NULL && isnan(ma)) return(2);
876
877 return(0);
878}

Referenced by tacInterpolateToEqualLengthFrames().

◆ tacIsX()

int tacIsX ( TAC * d)

Verify if TAC structure contains reasonable x values (times).

Data is not modified. Data does not need to be sorted. Data can contain NaNs, as long as some x values are available. Note that x values are considered not to exist even when there is only one sample with x=0.

See also
tacXRange, tacSetX, tacXCopy, tacCheckX1X2X, tacXNaNs, tacFixNaNs, tacMultipleSamples, tacVerifyTimeOrder, tacIsWeighted, tacIsXContiguous
Author
Vesa Oikonen
Returns
Returns 1 if x values are present, 0 if not (all zeroes or missing).
Parameters
dPointer to TAC structure; not modified.

Definition at line 226 of file tacx.c.

229 {
230 if(d==NULL || d->sampleNr<1) return(0);
231 double a, b;
232 if(tacXRange(d, &a, &b)) return(0);
233 if(fabs(a)<1.0E-30 && fabs(b)<1.0E-30) return(0);
234 if(fabs(a)<1.0E-30 && fabs(a-b)<1.0E-30) return(0);
235 return(1);
236}
int tacXRange(TAC *d, double *xmin, double *xmax)
Get the range of x values (times) in TAC structure.
Definition tacx.c:124

Referenced by tacInterpolateToEqualLengthFrames(), tacMultipleSamples(), tacReadReference(), tacSetWeights(), and tacSetX().

◆ tacIsXContiguous()

int tacIsXContiguous ( TAC * d)

Check that PET TAC frame times are contiguous, without even tiny overlap or gaps in between.

See also
tacSetXContiguous, tacIsX, tacXRange, tacVerifyTimeOrder, tacSortByTime, tacXCopy, tacMultipleSamples
Returns
If no overlap or gaps, TPCERROR_OK (0) is returned.
Author
Vesa Oikonen
Parameters
dPointer to TAC data which is sorted by increasing sample time.

Definition at line 1130 of file tacx.c.

1133 {
1134 if(d==NULL) return TPCERROR_FAIL;
1135 if(tacXNaNs(d)>0) return(TPCERROR_INVALID_X);
1136
1137 /* If no frame start and end times, then just check the order */
1138 if(d->isframe==0) {
1139 if(tacVerifyTimeOrder(d, NULL)) return(TPCERROR_INVALID_XRANGE);
1140 return TPCERROR_OK;
1141 }
1142
1143 /* Check each frame */
1144 for(int fi=0; fi<d->sampleNr-1; fi++) {
1145 double e=d->x2[fi]-d->x1[fi+1];
1146 if(e<0.0) return(TPCERROR_LARGE_GAP);
1147 if(e>0.0) return(TPCERROR_OVERLAPPING_DATA);
1148 }
1149
1150 return(TPCERROR_OK);
1151}
int tacXNaNs(TAC *tac)
Definition tacnan.c:23
@ TPCERROR_INVALID_XRANGE
Invalid sample time range.
@ TPCERROR_INVALID_X
Invalid sample time.
@ TPCERROR_LARGE_GAP
Large gap in data.

Referenced by tacFramesToSteps(), and tacSetXContiguous().

◆ tacMinX()

int tacMinX ( TAC * d)

Get the minimum x value in TAC structure.

Data does not need to be sorted, and it can contain missing x and y values (NaNs).

See also
tacSampleXRange, tacIsX, tacAddZeroSample
Returns
Returns the index of sample with min x, or -1 in case of an error.
Parameters
dPointer to the TAC structure; not modified.

Definition at line 201 of file tacx.c.

204 {
205 if(d==NULL || !(d->sampleNr>0)) return(-1);
206 int imin=-1; double xmin=nan("");
207 for(int i=0; i<d->sampleNr; i++) {
208 double t1; if(d->isframe) t1=0.5*(d->x1[i]+d->x2[i]); else t1=d->x[i];
209 if(!isfinite(t1)) continue;
210 if(!isfinite(xmin) || imin<0 || t1<xmin) {xmin=t1; imin=i; continue;}
211 }
212 return(imin);
213}

Referenced by tacMultipleSamples().

◆ tacSampleXRange()

int tacSampleXRange ( TAC * d,
double * xmin,
double * xmax )

Get the range of x values (times) in TAC structure.

Data is not modified. Data does not need to be sorted. Data can contain NaNs, and x values are omitted if no finite y values exist for that x. If data contains sample weights, samples with zero weight are not included.

See also
tacXRange, tacMinX, tacIsX, tacExtractRange, tacCorrectFrameOverlap, tacYRange, tacSetWeights, tacWSampleNr
Author
Vesa Oikonen
Returns
Returns <>0 in case of failure.
Parameters
dPointer to TAC structure; not modified.
xminPointer to variable for min x value (NULL if not needed).
xmaxPointer to variable for max x value (NULL if not needed).

Definition at line 162 of file tacx.c.

169 {
170 if(xmin!=NULL) *xmin=nan("");
171 if(xmax!=NULL) *xmax=nan("");
172 /* Check the data */
173 if(d==NULL || d->sampleNr<1) return(1);
174 /* Find the min and max time */
175 double mi, ma, *xi, *xa;
176 mi=ma=nan("");
177 if(d->isframe) {xi=d->x1; xa=d->x2;} else xi=xa=d->x;
178 int weighted=tacIsWeighted(d);
179 int i, j;
180 for(i=0; i<d->sampleNr; i++) {
181 if(!isfinite(xi[i]) || !isfinite(xa[i])) continue;
182 if(weighted && !(d->w[i]>0.0)) continue;
183 for(j=0; j<d->tacNr; j++) if(isfinite(d->c[j].y[i])) break;
184 if(j==d->tacNr) continue;
185 if(isnan(mi) || isnan(ma)) {mi=xi[i]; ma=xa[i]; continue;}
186 if(xi[i]<mi) mi=xi[i]; else if(xa[i]>ma) ma=xa[i];
187 }
188 if(xmin!=NULL) *xmin=mi;
189 if(xmax!=NULL) *xmax=ma;
190 if(!isfinite(mi) || !isfinite(ma)) return(2);
191 return(0);
192}
int tacIsWeighted(TAC *tac)
Definition tacw.c:24

Referenced by mtgaPlotSVG(), tacPlotFitSVG(), tacPlotHistogramSVG(), and tacPlotLineSVG().

◆ tacSetX()

int tacSetX ( TAC * d,
TPCSTATUS * status )

Set TAC x values based on x1 and x2 values, or guess x1 and x2 values based on x values.

This is not an idiot proof method, but works reasonably well in common cases.

See also
tacSortByTime, tacIsX, tacAddZeroSample, tacXCopy, tacCheckX1X2X, tacXNaNs, tacFixNaNs, tacIsXContiguous
Author
Vesa Oikonen
Returns
Returns TPCERROR status (0 if ok).
Parameters
dPointer to the TAC structure. TAC samples must be sorted for increasing x, if x1 and x2 are to be estimated from x values. TAC tunit should be specified as a time unit that can be internally converted to seconds to get most reliable results.
statusPointer to status data; enter NULL if not needed.

Definition at line 653 of file tacx.c.

661 {
662 int verbose=0; if(status!=NULL) verbose=status->verbose;
663 if(verbose>0) printf("%s()\n", __func__);
664
665 if(d==NULL) {
666 statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_FAIL);
667 return TPCERROR_FAIL;
668 }
669 if(d->sampleNr==0) {
670 statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_OK);
671 return TPCERROR_OK;
672 }
673 /* Check that some x values are there to begin with */
674 if(!tacIsX(d)) {
675 statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_NO_X);
676 return TPCERROR_NO_X;
677 }
678
679 /* If x1 and x2 are set, then calculate x for each sample */
680 if(d->isframe) {
681 if(verbose>1) printf("calculating x[] based on x1[] and x2[].\n");
682 int i, n, ret;
683 for(i=n=0; i<d->sampleNr; i++) {
684 d->x[i]=0.5*(d->x1[i]+d->x2[i]);
685 if(isfinite(d->x[i])) n++;
686 }
687 if(n>0) ret=TPCERROR_OK; else ret=TPCERROR_NO_X;
688 statusSet(status, __func__, __FILE__, __LINE__, ret);
689 return(ret);
690 }
691
692 /* We should start guessing x1 and x2 values based on their averages in x */
693 if(verbose>1) printf("estimating x1[] and x2[] based on x[].\n");
694
695 /* If reasonable x1 and x2 values already are set, then quit */
696 if(tacCheckX1X2X(d)) {
697 statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_OK);
698 return TPCERROR_OK;
699 }
700
701 /* If only one sample, then set x1=0 and x2=2*x, and quit */
702 if(d->sampleNr==1) {
703 if(verbose>2) printf("just one sample.\n");
704 if(d->x[0]<=0.0) {d->x1[0]=2.0*d->x[0]; d->x2[0]=0.0;}
705 else {d->x1[0]=0.0; d->x2[0]=2.0*d->x[0];}
706 statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_OK);
707 return TPCERROR_OK;
708 }
709
710 /* If only two samples */
711 if(d->sampleNr==2) {
712 if(verbose>2) printf("just two samples.\n");
713 if(d->x[0]>0.0 && 2.0*d->x[0]<0.95*d->x[1]) {
714 d->x1[0]=0.0; d->x2[0]=2.0*d->x[0];
715 d->x1[1]=d->x2[0]; d->x2[1]=d->x2[0]+2.0*(d->x[1]-d->x2[0]);
716 } else {
717 double f; f=d->x[1]-d->x[0];
718 d->x1[0]=d->x[0]-0.5*f; d->x2[0]=d->x[0]+0.5*f;
719 d->x1[1]=d->x2[0]; d->x2[1]=d->x2[0]+2.0*f;
720 }
721 statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_OK);
722 return TPCERROR_OK;
723 }
724
725 int i, n, ret;
726
727 /* Allocate playground as TAC struct t */
728 TAC t; tacInit(&t);
729 n=d->tacNr; d->tacNr=1; ret=tacDuplicate(d, &t); d->tacNr=n;
730 if(ret==0) ret=tacAllocateMore(&t, 1);
731 if(ret) {
732 statusSet(status, __func__, __FILE__, __LINE__, ret);
733 return(ret);
734 }
735 double *x, *x1, *x2, *fa, *fb;
736 x=t.x; x1=t.x1; x2=t.x2; fa=t.c[0].y; fb=t.c[1].y;
737 n=t.sampleNr;
738
739 /* Convert playground times to sec if possible */
740 tacXUnitConvert(&t, UNIT_SEC, NULL);
741
742 /* Set each x1 and x1 to x */
743 for(i=0; i<n; i++) x1[i]=x2[i]=x[i];
744
745 /* Calculate the min gap, excluding zeroes */
746 double gap, v;
747 gap=nan("");
748 for(i=0; i<n-1; i++) {
749 v=x[i+1]-x[i]; if(!isfinite(v) || v<0.001) continue;
750 if(isnan(gap)) {gap=v; continue;}
751 if(v<gap) gap=v;
752 }
753
754 if(!isfinite(gap)) {
755 tacFree(&t);
756 statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_NO_X);
757 return(TPCERROR_NO_X);
758 }
759 if(verbose>2) printf("smallest gap := %g\n", gap);
760 /* Set gap to smallest step value for frame lengths */
761 if(t.tunit!=UNIT_SEC) {
762 gap*=0.01;
763 } else {
764 /* Set to 0.5 sec, unless calculated gap is smaller */
765 if(gap>0.5) gap=0.5;
766 }
767 if(verbose>2) printf("stepsize := %g\n", gap);
768
769 /* Initiate frame lengths to 0 */
770 for(i=0; i<n; i++) fa[i]=fb[i]=0.0;
771 int fixed[n]; for(i=0; i<n; i++) fixed[i]=0;
772
773 /* Iterate until gaps are as small as possible */
774 int anr=0;
775 do {
776 anr=0;
777 /* Set tentative frames to the last accepted frame lengths */
778 for(i=0; i<n; i++) fa[i]=fb[i];
779 /* Add fraction of gap to frames that have not yet been fixed */
780 for(i=0; i<n; i++) if(!fixed[i]) fa[i]+=gap;
781 /* Calculate x1 and x2 based on the tentative frames */
782 for(i=0; i<n; i++) {x1[i]=x[i]-0.5*fa[i]; x2[i]=x[i]+0.5*fa[i];}
783 /* If no overlap, then copy frame */
784 i=0;
785 if(x2[i]<=x1[i+1]+0.001*gap && (x[i]<=0.0 || x1[i]>-0.001*gap)) {
786 fb[i]=fa[i]; if(!fixed[i]) anr++;
787 } else
788 fixed[i]=1;
789 for(i=1; i<n-1; i++) {
790 if(x2[i]<=x1[i+1]+0.001*gap && x1[i]>=x2[i-1]-0.001*gap) {
791 fb[i]=fa[i]; if(!fixed[i]) anr++;} else fixed[i]=1;
792 }
793 if(x1[i]>=x2[i-1]-0.001*gap) {
794 fb[i]=fa[i]; if(!fixed[i]) anr++;} else fixed[i]=1;
795 if(verbose>5) {
796 printf("frames:");
797 for(i=0; i<n; i++) printf(" %g", fb[i]);
798 printf("\n");
799 }
800 } while(anr>0);
801
802 if(verbose>6) {
803 printf("temp frames:");
804 for(i=0; i<n; i++) printf(" %g", fa[i]);
805 printf("\n");
806 }
807
808 /* Calculate final frames */
809 for(i=0; i<n; i++) {x1[i]=x[i]-0.5*fb[i]; x2[i]=x[i]+0.5*fb[i];}
810
811 /* Convert playground to original time units */
812 tacXUnitConvert(&t, d->tunit, NULL);
813
814 /* Copy the guessed x1 and x2 values from playground */
815 t.isframe=1; tacXCopy(&t, d, 0, d->sampleNr-1);
816
817 /* Delete playground TAC */
818 tacFree(&t);
819
820 statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_OK);
821 return TPCERROR_OK;
822}
int tunit
Definition tpctac.h:109
int tacAllocateMore(TAC *tac, int tacNr)
Definition tac.c:178
int tacXUnitConvert(TAC *tac, const int u, TPCSTATUS *status)
Definition tacunits.c:23
int tacXCopy(TAC *tac1, TAC *tac2, int i1, int i2)
Definition tacx.c:24
int tacIsX(TAC *d)
Verify if TAC structure contains reasonable x values (times).
Definition tacx.c:226
int tacCheckX1X2X(TAC *d)
Check that sample (time frame) x, x1, and x2 values are reasonably set when compared to each other in...
Definition tacx.c:621
@ UNIT_SEC
seconds
@ TPCERROR_NO_X
No sample times.

Referenced by tacInput2sim().

◆ tacSetXContiguous()

int tacSetXContiguous ( TAC * d)

Set PET TAC frame times contiguous, without even tiny overlap or gaps in between.

Tiny overlaps and gaps are fixed by modifying frame times. Large gaps are filled with new frames based on linear interpolation between adjacent frames. Error is returned in case of larger frame overlaps.

See also
tacIsXContiguous, tacIsX, tacXRange, tacVerifyTimeOrder, tacSortByTime, tacMultipleSamples
Returns
If successful, TPCERROR_OK (0) is returned.
Author
Vesa Oikonen
Parameters
dPointer to TAC data which is sorted by increasing sample time.

Definition at line 1166 of file tacx.c.

1169 {
1170 if(d==NULL) return TPCERROR_FAIL;
1171 if(tacXNaNs(d)>0) return(TPCERROR_INVALID_X);
1172
1173 /* If no frame start and end times, then just make the times increasing */
1174 if(d->isframe==0) return(tacSortByTime(d, NULL));
1175
1176 /* If there are no gaps or overlap, then we're done */
1178
1179 /* Make a duplicate for editing */
1180 TAC tac; tacInit(&tac);
1181 int ret=tacDuplicate(d, &tac); if(ret!=TPCERROR_OK) return(ret);
1182
1183 /* Check for small frame overlaps or gaps; fix small, and return error if large overlap */
1184 int fixed=0, large=0;
1185 for(int fi=0; fi<d->sampleNr-1; fi++) {
1186 double dif=d->x1[fi+1]-d->x2[fi]; if(dif==0.0) continue; // not gap or overlap
1187 double fdur1=d->x2[fi]-d->x1[fi];
1188 double fdur2=d->x2[fi+1]-d->x1[fi+1];
1189 if(dif>0) { // gap
1190 if(dif<0.2*fdur1 && dif<0.2*fdur2) { // small gap; increase the shorter frame duration
1191 if(fdur1<fdur2) tac.x2[fi]=d->x1[fi+1]; else tac.x1[fi+1]=d->x2[fi];
1192 fixed++;
1193 } else {
1194 large++; // large gap not fixed yet
1195 }
1196 continue;
1197 }
1198 /* Overlap */
1199 dif=-dif;
1200 if(dif>0.2*fdur1 || dif>0.2*fdur2) { // too large overlap
1201 tacFree(&tac); return(TPCERROR_OVERLAPPING_DATA);
1202 }
1203 /* Reduce the longer frame length */
1204 if(fdur1>fdur2) tac.x2[fi]=d->x1[fi+1]; else tac.x1[fi+1]=d->x2[fi];
1205 fixed++;
1206 }
1207
1208 /* If we fixed something, then copy the fixed frame times */
1209 if(fixed>0) {
1210 for(int i=0; i<d->sampleNr; i++) {
1211 d->x1[i]=tac.x1[i]; d->x2[i]=tac.x2[i]; d->x[i]=0.5*(d->x1[i]+d->x2[i]);
1212 }
1213 }
1214
1215 /* If there were no large gaps, we're done */
1216 if(large==0) {tacFree(&tac); return(TPCERROR_OK);}
1217
1218 /* Fill large gaps with new frames */
1220 if(tacAllocateMoreSamples(d, large)!=0) {
1221 tacFree(&tac); return(TPCERROR_OUT_OF_MEMORY);
1222 }
1223 d->sampleNr=1;
1224 for(int fi=1; fi<tac.sampleNr; fi++) {
1225 double dif=tac.x1[fi]-tac.x2[fi-1];
1226 if(dif>0.0) {
1227 /* Add new frame times */
1228 d->x1[d->sampleNr]=tac.x2[fi-1]; d->x2[d->sampleNr]=tac.x1[fi];
1229 d->x[fi]=0.5*(d->x1[d->sampleNr]+d->x2[d->sampleNr]);
1230 /* Calculate y values for the new frame */
1231 double x1=0.5*(tac.x1[fi-1]+tac.x2[fi-1]);
1232 double x2=0.5*(tac.x1[fi]+tac.x2[fi]);
1233 for(int ri=0; ri<tac.tacNr; ri++) {
1234 double s=(tac.c[ri].y[fi]-tac.c[ri].y[fi-1])/(x2-x1);
1235 d->c[ri].y[d->sampleNr]=tac.c[ri].y[fi-1] + s*(d->x[fi]-x1);
1236 }
1237 d->sampleNr++;
1238 }
1239 /* copy the existing frame */
1240 d->x1[d->sampleNr]=tac.x1[fi]; d->x2[d->sampleNr]=tac.x2[fi];
1241 for(int ri=0; ri<tac.tacNr; ri++) d->c[ri].y[d->sampleNr]=tac.c[ri].y[fi];
1242 d->sampleNr++;
1243 }
1244
1245 tacFree(&tac);
1246 return(TPCERROR_OK);
1247}
int tacSortByTime(TAC *d, TPCSTATUS *status)
Definition tacorder.c:74
@ WEIGHTING_OFF
Not weighted or weights not available (weights for all included samples are 1.0).

◆ tacToBars()

int tacToBars ( TAC * tac1,
TAC * tac2 )

Transform TAC data with frame start and end times into suitable form for plotting with frames as bars.

Attention
Output data must not be used for quantitative purposes.
See also
tacFramesToSteps, tacXRange, tacIsX, tacCheckX1X2X, tacExtractRange, tacSetXContiguous
Returns
Returns <>0 in case of failure.
Parameters
tac1Pointer to input TAC structure; not modified. Must be sorted by increasing x.
tac2Pointer to output TAC structure; must be initiated; any previous content is destroyed.
See also
tacInit, tacFree

Definition at line 888 of file tacx.c.

894 {
895 if(tac1==NULL || tac2==NULL || tac1->sampleNr<1 || tac1->tacNr<1) return(1);
896 /* Make sure that frame start and end times are present */
897 if(tac1->isframe==0) return(2);
898 /* Remove any old contents in target struct */
899 tacFree(tac2);
900
901 /* Make a copy of the TAC data */
902 if(tacDuplicate(tac1, tac2)!=0) return(3);
903 /* Add room for more frames; we plot each frame as its own bar
904 and therefore we may need 4x the original space */
905 if(tacAllocateMoreSamples(tac2, 3*tac1->sampleNr)!=0) return(4);
906
907 /* Make separate 'samples' for each frame start and end time */
908 tac2->isframe=0;
909 int fj=0;
910 for(int fi=0; fi<tac1->sampleNr; fi++) {
911 /* Do nothing if missing values */
912 if(isnan(tac1->x1[fi]) || isnan(tac1->x2[fi])) continue;
913 if(tac1->tacNr==1 && isnan(tac1->c[0].y[fi])) continue;
914 /* Otherwise make bar of each frame */
915 tac2->x[fj]=tac1->x1[fi];
916 for(int ri=0; ri<tac2->tacNr; ri++) tac2->c[ri].y[fj]=0.0;
917 fj++;
918 tac2->x[fj]=tac1->x1[fi];
919 for(int ri=0; ri<tac2->tacNr; ri++) tac2->c[ri].y[fj]=tac1->c[ri].y[fi];
920 fj++;
921 tac2->x[fj]=tac1->x2[fi];
922 for(int ri=0; ri<tac2->tacNr; ri++) tac2->c[ri].y[fj]=tac1->c[ri].y[fi];
923 fj++;
924 tac2->x[fj]=tac1->x2[fi];
925 for(int ri=0; ri<tac2->tacNr; ri++) tac2->c[ri].y[fj]=0.0;
926 fj++;
927 }
928 tac2->sampleNr=fj;
929 if(fj<1) {tacFree(tac2); return(5);}
930 return(0);
931}

◆ tacXCopy()

int tacXCopy ( TAC * tac1,
TAC * tac2,
int i1,
int i2 )

Copy x values (frame times) from one TAC structure to another.

See also
tacCompareTimes, tacXMatch, tacIsX, tacCheckX1X2X, tacInterpolate, tacWCopy, tacCopyTacc
Author
Vesa Oikonen
Returns
Returns TPCERROR status.
Parameters
tac1Pointer to source TAC structure.
tac2Pointer to target TAC structure.
i1Index of the first sample time to copy.
i2Index of the last sample time to copy.

Definition at line 24 of file tacx.c.

33 {
34 if(tac1==NULL || tac2==NULL) return(TPCERROR_FAIL);
35 if(i1<0 || i1>i2) return(TPCERROR_FAIL);
36 if(i2>=tac1->_sampleNr || i2>=tac2->_sampleNr) return(TPCERROR_FAIL);
37
38 for(int i=i1; i<=i2; i++) {
39 tac2->x[i]=tac1->x[i];
40 tac2->x1[i]=tac1->x1[i];
41 tac2->x2[i]=tac1->x2[i];
42 }
43 return(TPCERROR_OK);
44}

Referenced by bfm1TCM(), tacAllocateMoreSamples(), tacDuplicate(), tacExtract(), tacInterpolate(), tacReadReference(), tacSetWeights(), and tacSetX().

◆ tacXMatch()

int tacXMatch ( TAC * d1,
TAC * d2,
const int verbose )

Check whether sample (frame) times are the same (or very close to) in two TAC structures.

Data is not modified. Sort by sample time before calling this. The order of TAC structures is not important. If sampleNr is different, then only common number of samples are compared.

See also
tacCompareTimes, tacXCopy, tacSortByTime, tacIsX
Author
Vesa Oikonen
Returns
Returns 1 if times do match, 0 if not.
Parameters
d1Pointer to the first TAC structure; not modified.
d2Pointer to the second TAC structure; time unit does not matter if conversion can be done. Sample nr may be different than in tac1. Contents not modified.
verboseVerbose level; if zero, then nothing is printed to stderr or stdout.

Definition at line 249 of file tacx.c.

257 {
258 if(verbose>0) printf("\n%s()\n", __func__);
259
260 /* Check data */
261 if(d1==NULL || d2==NULL) return 0;
262 double xmin[2], xmax[2];
263 if(tacXRange(d1, xmin, xmax) || tacXRange(d2, xmin+1, xmax+1)) return(0);
264
265 /* Which has less samples? */
266 int smaller_sampleNr;
267 if(d1->sampleNr<d2->sampleNr) smaller_sampleNr=d1->sampleNr;
268 else smaller_sampleNr=d2->sampleNr;
269 if(verbose>1) printf("smaller_sampleNr := %d\n", smaller_sampleNr);
270 if(smaller_sampleNr<=0) return 0;
271
272 /* Get conversion factors for sample times to get those in sec */
273 double ts1, ts2;
275 if(isnan(ts1)) {
276 if(verbose>1) printf("cannot convert tac1 sample times to sec.\n");
277 ts1=1.0; if(d1->tunit==UNIT_UNKNOWN) ts1=60.0; // assuming min
278 }
280 if(isnan(ts2)) {
281 if(verbose>1) printf("cannot convert tac2 sample times to sec.\n");
282 ts2=1.0; if(d2->tunit==UNIT_UNKNOWN) ts2=60.0; // assuming min
283 }
284 /* Convert time ranges to sec */
285 xmin[0]*=ts1; xmax[0]*=ts1;
286 xmin[1]*=ts2; xmax[1]*=ts2;
287 if(verbose>1) {
288 printf("tac1->isframe := %d\n", d1->isframe);
289 if(verbose>2) printf("time_range1 := %g - %g\n", xmin[0], xmax[0]);
290 printf("tac2->isframe := %d\n", d2->isframe);
291 if(verbose>2) printf("time_range2 := %g - %g\n", xmin[1], xmax[1]);
292 }
293
294 /* Set the accepted time difference */
295 double f, accepted_timedif=2.2; // sec
296 /* With short study the accepted time difference must be shorter */
297 f=xmax[0]-xmin[0]; if((xmax[1]-xmin[1])>f) f=xmax[1]-xmin[1];
298 f*=0.01; if(accepted_timedif>f) accepted_timedif=f;
299 if(verbose>1) {
300 printf("accepted_timedif := %g [s]\n", accepted_timedif);
301 fflush(stdout);
302 }
303
304 /* Compare sample times frame-by-frame */
305 int fi, n=0;
306 if(d1->isframe==0 && d2->isframe==0) {
307 if(verbose>2) {printf("frame mid times\n"); fflush(stdout);}
308 for(fi=0; fi<smaller_sampleNr; fi++) {
309 if(d1->x[fi]*ts1==d2->x[fi]*ts2) continue;
310 if(isnan(d1->x[fi]) && isnan(d2->x[fi])) continue; // match if both NaN
311 if(isnan(d1->x[fi]) || isnan(d2->x[fi])) {n++; continue;} // no match
312 f=fabs(d1->x[fi]*ts1-d2->x[fi]*ts2);
313 if(verbose>3) printf("timedif[%d] := %g\n", fi, f);
314 if(verbose>4) printf(" %g vs %g\n", ts1*d1->x[fi], ts2*d2->x[fi]);
315 if(f>accepted_timedif) n++;
316 }
317 } else if(d1->isframe!=0 && d2->isframe!=0) {
318 if(verbose>2) {printf("frame start and end times\n"); fflush(stdout);}
319 for(fi=0; fi<smaller_sampleNr; fi++) {
320 if(d1->x1[fi]*ts1==d2->x1[fi]*ts2) continue;
321 if(isnan(d1->x1[fi]) && isnan(d2->x1[fi])) continue; // match if both NaN
322 if(isnan(d1->x1[fi]) || isnan(d2->x1[fi])) {n++; continue;} // no match
323 f=fabs(d1->x1[fi]*ts1-d2->x1[fi]*ts2);
324 if(verbose>3) printf("timedif[%d] := %g\n", fi, f);
325 if(verbose>4) printf(" %g vs %g\n", ts1*d1->x1[fi], ts2*d2->x1[fi]);
326 if(f>accepted_timedif) n++;
327 }
328 for(fi=0; fi<smaller_sampleNr; fi++) {
329 if(d1->x2[fi]*ts1==d2->x2[fi]*ts2) continue;
330 if(isnan(d1->x2[fi]) && isnan(d2->x2[fi])) continue; // match if both NaN
331 if(isnan(d1->x2[fi]) || isnan(d2->x2[fi])) {n++; continue;} // no match
332 f=fabs(d1->x2[fi]*ts1-d2->x2[fi]*ts2);
333 if(verbose>3) printf("timedif[%d] := %g\n", fi, f);
334 if(verbose>4) printf(" %g vs %g\n", ts1*d1->x2[fi], ts2*d2->x2[fi]);
335 if(f>accepted_timedif) n++;
336 }
337 } else {
338 if(verbose>2) {printf("mixed frame times\n"); fflush(stdout);}
339 double x1, x2;
340 for(fi=0; fi<smaller_sampleNr; fi++) {
341 if(d1->isframe) x1=0.5*(d1->x1[fi]+d1->x2[fi]); else x1=d1->x[fi];
342 if(d2->isframe) x2=0.5*(d2->x1[fi]+d2->x2[fi]); else x2=d2->x[fi];
343 if(x1*ts1==x2*ts2) continue;
344 if(isnan(x1) && isnan(x2)) continue; // match if both NaN
345 if(isnan(x1) || isnan(x2)) {n++; continue;} // no match
346 f=fabs(x1*ts1-x2*ts2);
347 if(verbose>3) printf("timedif[%d] := %g\n", fi, f);
348 if(verbose>4) printf(" %g vs %g\n", x1, x2);
349 if(f>accepted_timedif) n++;
350 }
351 }
352 if(verbose>2) {printf("nr of different frame times := %d\n", n); fflush(stdout);}
353 if(n==0) return 1; else return 0;
354}
@ UNIT_UNKNOWN
Unknown unit.
double unitConversionFactor(const int u1, const int u2)
Definition units.c:487

Referenced by tacInterpolate(), tacInterpolateInto(), and tacReadReference().

◆ tacXRange()

int tacXRange ( TAC * d,
double * xmin,
double * xmax )

Get the range of x values (times) in TAC structure.

Data is not modified. Data does not need to be sorted. Data can contain NaNs, but x values are not omitted if y is NaN and x is not. Samples with zero weight are included.

See also
tacSampleXRange, tacIsX, tacExtractRange, tacCorrectFrameOverlap, tacYRange
Author
Vesa Oikonen
Returns
Returns <>0 in case of failure.
Parameters
dPointer to TAC struct
xminPointer to variable for min x value (NULL if not needed).
xmaxPointer to variable for max x value (NULL if not needed).

Definition at line 124 of file tacx.c.

131 {
132 if(xmin!=NULL) *xmin=nan("");
133 if(xmax!=NULL) *xmax=nan("");
134 /* Check the data */
135 if(d==NULL || d->sampleNr<1) return(1);
136 /* Find the min and max time */
137 double mi, ma, *xi, *xa;
138 mi=ma=nan("");
139 if(d->isframe) {xi=d->x1; xa=d->x2;} else xi=xa=d->x;
140 for(int i=0; i<d->sampleNr; i++) {
141 if(!isfinite(xi[i]) || !isfinite(xa[i])) continue;
142 if(isnan(mi) || isnan(ma)) {mi=xi[i]; ma=xa[i]; continue;}
143 if(xi[i]<mi) mi=xi[i]; else if(xa[i]>ma) ma=xa[i];
144 }
145 if(xmin!=NULL) *xmin=mi;
146 if(xmax!=NULL) *xmax=ma;
147 if(!isfinite(mi) || !isfinite(ma)) return(2);
148 return(0);
149}

Referenced by sifWeight(), tacAUC(), tacDelay(), tacDelayFit(), tacFittime(), tacInterpolate(), tacInterpolateInto(), tacInterpolateToEqualLengthFrames(), tacIsX(), tacReadModelingData(), and tacXMatch().