38 #include "apd_cmdline.h"
52 lininterp(
float y0,
float y1,
float x0,
float x1,
float interp )
54 float m = (y1-y0)/(x1-x0);
61 void output_apd( FILE *out,
int n,
float *o1,
float *o2=NULL,
float o1scale=1 ){
64 for(
int i=0; i<n; i++ )
65 fprintf( out,
"%f\t%f\n", o1[i]*o1scale, o2[i] );
67 for(
int i=0; i<n; i++ )
68 fprintf( out,
"%f\n", o1[i]*o1scale );
97 if( strchr( sp,
':' ) ){
98 char *colptr = strchr( sp,
':' );
100 sscanf( colptr+1,
"%d%n", &inc, &consumed );
101 if( !consumed || consumed!=strlen(colptr+1) ) {
102 fprintf( stderr,
"Illegal range specified: %s\n", sp );
108 if( !strcmp(
"*", sp ) ) {
111 for(
int i=0; i<
max; i+=inc )
112 nodes.insert(nodes.end(),i);
114 for(
int i=0; i<
max; i+=inc )
120 if( strchr( sp,
'-' ) ){
123 int nr = sscanf( sp,
"%d-%s", &s, fs );
125 fprintf( stderr,
"Illegal range specified: %s\n", sp );
130 if( !strcmp(fs,
"*") )
134 sscanf( fs,
"%d%n", &f, &consumed );
135 if( consumed != strlen(fs) ) {
136 fprintf( stderr,
"Illegal range specified: %s\n", sp );
142 fprintf( stderr,
"Illegal range specified: %s\n", sp );
146 fprintf( stderr,
"Illegal range, maximum node number exceeded: %s\n", sp );
149 for(
int i=s; i<=f; i+=inc )
155 sscanf( sp,
"%d", &num );
157 if( !sscanf( sp,
"%d", &num ) || num<0 || num>=
max ) {
158 fprintf( stderr,
"Illegal node number specified in list: %d\n", num );
170 char *lc = strdup( list );
171 char *sp = strtok( lc,
"," );
174 while( sp != NULL ) {
178 sp = strtok( NULL,
"," );
182 if( !ch_nodes.size() ) {
183 fprintf( stderr,
"No nodes specified: %s\n", list );
188 nodes.resize( ch_nodes.size() );
190 auto end=ch_nodes.end();
191 for(
auto sit=ch_nodes.cbegin(); sit!=end; sit++ )
200 gengetopt_args_info args;
203 if (cmdline_parser (argc, argv, &args) != 0)
210 if( args.inputs_num && strcmp(args.inputs[0],
"-") )
211 fin = fopen( args.inputs[0],
"r" );
215 cerr <<
"File is not a proper IGB file: "<< args.inputs[0] << endl;
219 if( args.first_given )
220 first = args.first_arg;
221 else if( args.first_time_given )
222 first = (args.first_time_arg-h->
org_t())/h->
inc_t();
225 if( strcmp(args.output_file_arg,
"-") )
226 out = fopen( args.output_file_arg,
"w" );
231 if( args.signal_arg == signal_arg_AP )
233 else if( args.signal_arg == signal_arg_Uni )
235 else if( args.signal_arg == signal_arg_Bip )
261 float* phi = (
float*)calloc(h->
slice_sz(),
sizeof(float));
262 float* oldphi = (
float*)calloc(h->
slice_sz(),
sizeof(float));
263 float* olderphi = (
float*)calloc(h->
slice_sz(),
sizeof(float));
267 vector<float> prevlat(h->
slice_sz(),-2*args.minapd_arg);
268 vector<deque<float>> actwin(nodes.size());
271 float DVDT_THRESH = args.dvdt_arg*h->
inc_t()*2;
272 if( args.debug_flag ) std::cout <<
"DVDT_THRESH: " << DVDT_THRESH << std::endl;
274 int unfound = nodes.size();
279 for(
int t=0; t<first-1; t++ )
283 for(
int t=first; t<h->
t() && unfound; t++ ){
287 #pragma omp parallel for
288 for(
int j=0; j<nodes.size(); j++ ) {
291 float magphi = fabs(oldphi[n]);
292 float dphi = fabs(phi[n]-olderphi[n]);
293 if( actwin[j].size() == args.act_window_arg ) actwin[j].pop_front();
294 actwin[j].push_back(magphi);
295 bool active=*std::max_element(actwin[j].begin(),actwin[j].end())>args.phimin_arg;
298 if( active && dphi>=DVDT_THRESH ) {
299 act[n] = lat[n] = t-1;
301 if( args.debug_flag ) std::cout << n <<
" act:" << act[n] <<
" mag:" <<
302 magphi<<
" dphi/dt:" << dphi << std::endl;
304 }
else if( maxphi[n] ==
FINISHED ) {
306 }
else if( active ) {
307 if( magphi>maxphi[n] ) {
311 }
else if( !active ) {
312 if( t-1-act[n]>=args.act_dur_min_arg &&
313 t-1-act[n]<=args.act_dur_max_arg &&
314 t-prevlat[n]>=args.minapd_arg ) {
315 if( args.debug_flag ) std::cout << n <<
" : @" << t <<
" duration:" <<
316 t-1-act[n] <<
" prev:" << prevlat[n] <<endl;
317 if( args.mode_arg == mode_arg_first ||
318 args.mode_arg == mode_arg_act_first ) {
321 if( args.debug_flag ) std::cout << n <<
" act: " << lat[n] << std::endl;
323 cout << n <<
" : " << lat[n]+h->
org_t() << std::endl;
329 if( args.debug_flag ) std::cout << n <<
" abandoned: @" << t <<
" duration:" <<
330 t-1-act[n] <<
" prev:" << prevlat[n] << std::endl;
336 auto *tmp = olderphi;
341 for(
int i=0; i<lat.size(); i++ ) lat[i]+=h->
org_t();
342 if( args.mode_arg == mode_arg_first ||
343 args.mode_arg == mode_arg_act_first )
350 float* phi = (
float*)calloc(h->
slice_sz(),
sizeof(float));
351 float* oldphi = (
float*)calloc(h->
slice_sz(),
sizeof(float));
352 float* olderphi = (
float*)calloc(h->
slice_sz(),
sizeof(float));
356 vector<float> mindphi(h->
slice_sz());
360 float DVDT_THRESH = args.dvdt_arg*h->
inc_t()*2;
361 float DVDT_THRESH_REPOL = args.dvdt_repol_arg*h->
inc_t()*2;
363 int unfound = nodes.size();
368 for(
int t=0; t<first-2; t++ )
374 for(
int t=first; t<h->
t() && unfound; t++ ){
378 #pragma omp parallel for
379 for(
int j=0; j<nodes.size(); j++ ) {
382 float dphi = phi[i]-olderphi[i];
385 if( dphi <= DVDT_THRESH ) {
390 }
else if ( apstart[i] !=
FINISHED ) {
391 if( t-apstart[i]<=args.upstroke_dur_arg && dphi<mindphi[i] ) {
394 }
else if( t-apstart[i]>args.maxapd_arg ) {
395 if( repol[i] !=
UNCOMPUTED ) apd[i] = repol[i] - lat[i];
396 if( args.mode_arg == mode_arg_first ) {
398 }
else if( args.mode_arg == mode_arg_all ) {
399 cout << i <<
" : " << apd[i];
400 if( args.start_times_flag )
401 cout <<
"\t\tstarted : " << lat[i] + h->
org_t();
403 }
else if( args.mode_arg == mode_arg_repol_all ) {
404 cout << i <<
" : " << repol[i] + h->
org_t();
405 if( args.start_times_flag )
406 cout <<
"\t\tstarted : " << lat[i] + h->
org_t();
408 }
else if( args.mode_arg == mode_arg_repol_first ) {
410 repol[i] += h->
org_t();
412 }
else if( args.mode_arg == args.debug_flag ) {
413 cout <<
"node:" << i <<
"\tAPD: " << apd[i] <<
"\tstarted: " << lat[i] + h->
org_t();
416 if( (args.mode_arg==mode_arg_all || args.debug_flag ||
417 args.mode_arg==mode_arg_repol_all ) ){
423 }
else if( t-apstart[i]>args.minapd_arg ) {
424 if( dphi>=DVDT_THRESH_REPOL &&
432 float *tmp = olderphi;
437 for(
int i=0; i<lat.size(); i++ ) lat[i]+=h->
org_t();
445 float DVDT_THRESH = args.dvdt_arg*h->
inc_t();
446 float repolarize = args.repol_arg;
447 float v_thresh = args.rep_level_arg;
448 float plusthresh = 10.;
451 if( args.threshold_mode_counter )
456 int MINAPD = args.minapd_arg;
457 if( args.peak_value_arg == peak_value_arg_plateau )
458 if( MINAPD<args.plateau_duration_arg + args.plateau_start_arg ) {
459 MINAPD += args.plateau_duration_arg + args.plateau_start_arg;
460 cerr <<
"warning: changing apdmin to "<<MINAPD<<
" from "<<MINAPD<<endl;
463 float *oldervm = (
float *)calloc(h->
slice_sz(),
sizeof(float));
464 float *oldvm = (
float *)calloc(h->
slice_sz(),
sizeof(float));
465 float *vm = (
float *)calloc(h->
slice_sz(),
sizeof(float));
466 float *apd = (
float *)calloc(h->
slice_sz(),
sizeof(float));
467 float *vmthresh = (
float *)calloc(h->
slice_sz(),
sizeof(float));
468 float *vmrest = (
float *)calloc(h->
slice_sz(),
sizeof(float));
469 float *integral = args.integral_flag ? (
float *)calloc(h->
slice_sz(),
sizeof(float)) : NULL;
470 bool *peaked = (
bool *)calloc(h->
slice_sz(),
sizeof(bool));
471 vector<float> repol_tm( h->
slice_sz(),-1.);
474 int *last_apstart = NULL;
475 if( args.mode_arg==mode_arg_act) last_apstart = (
int *)calloc(h->
slice_sz(),
sizeof(int));
477 for(
int i=0; i<h->
slice_sz(); i++ ) {
481 vmthresh[i] = threshtype==
MinDeriv?0:v_thresh;
482 if( args.mode_arg==mode_arg_act) last_apstart[i] =
NO_PRIOR;
485 int unfound = nodes.size();
487 for(
int t=0; t<first; t++ )
490 for(
int t=first; t<h->
t() && unfound; t++ ){
495 memcpy( oldvm, vm, h->
slice_sz()*
sizeof(
float) );
497 #pragma omp parallel for
498 for(
int j=0; j<nodes.size(); j++ ) {
505 if( t && fabs(vm[i]-oldervm[i])<0.001 )
506 vmrest[i] = oldvm[i];
508 if( (vm[i]-oldvm[i])>DVDT_THRESH && vm[i]>args.vup_arg ) {
510 if( oldvm[i]<=args.vup_arg )
511 apdstart[i] =
lininterp( oldvm[i], vm[i], t-1, t, args.vup_arg )*h->
inc_t();
513 apdstart[i] =
lininterp( oldvm[i]-oldervm[i], vm[i]-oldvm[i],
514 t-1, t, DVDT_THRESH )*h->
inc_t();
517 if( args.mode_arg == mode_arg_detect ) {
518 cout << i <<
" @ " << apdstart[i]+h->
org_t() << endl;
525 if( !peaked[i] && apdstart[i]!=
UNSTARTED ) {
526 switch( threshtype ) {
532 if( vm[i]<oldvm[i] ) {
534 vmthresh[i] = vmrest[i]+plusthresh;
538 if( args.peak_value_arg == peak_value_arg_upstroke ) {
539 if( vm[i]<oldvm[i] ) {
541 vmthresh[i] = vmrest[i] + (1.-repolarize/100.)*(oldvm[i] -vmrest[i] );
544 if( t*h->
inc_t()-apdstart[i] < args.plateau_start_arg ) {
546 }
else if( t*h->
inc_t()-apdstart[i] < args.plateau_start_arg+args.plateau_duration_arg ) {
547 if( vm[i] > vmthresh[i] )
550 vmthresh[i] = vmrest[i] + (1.-repolarize/100.)*(vmthresh[i] -vmrest[i] );
559 if( peaked[i] && vm[i]-vmrest[i]<args.mindv_arg ) {
562 if( args.debug_flag )
563 cout <<
"node: " << i <<
" failed to reach min dv/dt at frame " << t<< endl;
567 if( peaked[i] && apd[i]==
UNCOMPUTED && args.mode_arg==mode_arg_act_first ) {
569 }
else if( peaked[i] && apd[i]==
UNCOMPUTED && args.mode_arg==mode_arg_act) {
571 if( last_apstart[i]==
NO_PRIOR || (t-last_apstart[i])*h->
inc_t()>MINAPD ) {
572 cout << i <<
" : " << apdstart[i]+h->
org_t() << endl;
577 }
else if( peaked[i] && apd[i]==
UNCOMPUTED ) {
578 if( args.integral_flag ) integral[i] += vm[i]-vmrest[i];
581 if( threshtype==
MinDeriv && oldervm[i]-oldvm[i] > oldvm[i]-vm[i] &&
582 oldervm[i]-oldvm[i]>vmthresh[i] ) {
583 vmthresh[i] = oldervm[i] -oldvm[i] ;
584 apd[i] = (t - 1)*h->
inc_t() - apdstart[i];
586 }
else if(vm[i]<=vmthresh[i] && oldvm[i]>vmthresh[i] ) {
587 apd[i] =
lininterp( oldvm[i], vm[i], t-1, t, vmthresh[i] )*h->
inc_t()-apdstart[i];
591 if( apd[i] < MINAPD ) {
595 if( args.integral_flag ) integral[i] = 0.;
596 if( args.debug_flag )
597 cerr <<
"node: " << i <<
" APD too short at frame " << t<< endl;
598 }
else if( args.mode_arg == mode_arg_first ) {
600 }
else if( args.mode_arg == mode_arg_all ) {
601 cout << i <<
" : " << (args.integral_flag ? integral[i]*h->
inc_t() : apd[i]);
602 if( args.start_times_flag )
603 cout <<
"\t\tstarted : " << apdstart[i] + h->
org_t();
605 }
else if( args.mode_arg == mode_arg_repol_all ) {
606 cout << i <<
" : " << apd[i] + apdstart[i] + h->
org_t();
607 if( args.start_times_flag )
608 cout <<
"\t\tstarted : " << apdstart[i] + h->
org_t();
610 }
else if( args.mode_arg == mode_arg_repol_first ){
611 if( repol_tm[i] < 0. ) {
612 repol_tm[i] =apd[i] + apdstart[i] + h->
org_t();
614 }
else if( args.debug_flag ) {
615 cout <<
"node:" << i <<
"\tAPD: " << apd[i] <<
"\tstarted: " << apdstart[i] + h->
org_t();
616 cout <<
"\trest: " << vmrest[i];
617 cout <<
"\tvmthresh: " << vmthresh[i];
623 if( (args.mode_arg==mode_arg_all || args.debug_flag ||
624 args.mode_arg==mode_arg_repol_all ) &&
625 apd[i]!=
UNCOMPUTED && t*h->
inc_t()-apdstart[i]+apd[i]>args.blank_arg ) {
628 if( args.integral_flag ) integral[i] = 0.;
633 float *tmp = oldervm;
639 for(
int i=0; i<apdstart.size(); i++ ) apdstart[i]+=h->
org_t();
641 if( args.mode_arg == mode_arg_first ) {
643 args.integral_flag ? integral : apd,
644 args.start_times_flag ? apdstart.data() : NULL,
645 args.integral_flag ? h->
inc_t() : 1. );
646 }
else if( args.mode_arg == mode_arg_repol_first ) {
647 output_apd( out, h->
slice_sz(), repol_tm.data(), args.start_times_flag?apdstart.data():NULL );
648 }
else if( args.mode_arg == mode_arg_act_first ) {
void extract_nodes(const char *list, vector< int > &nodes, int max)
int main(int argc, char *argv[])
void process_unipoles(gengetopt_args_info &, IGBheader *, vector< int > &, FILE *, int)
void process_action_potential(gengetopt_args_info &, IGBheader *, vector< int > &, FILE *, int)
float lininterp(float y0, float y1, float x0, float x1, float interp)
void process_bipoles(gengetopt_args_info &, IGBheader *, vector< int > &, FILE *, int)
determine activations in bipolar recordings
bool process_specifier(char *sp, set< int > &nodes, int max)
void output_apd(FILE *out, int n, float *o1, float *o2=NULL, float o1scale=1)
constexpr T max(T a, T b)