openCARP
Doxygen code documentation for the open cardiac electrophysiology simulator openCARP
IGBapd.cc
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // openCARP is an open cardiac electrophysiology simulator.
3 //
4 // Copyright (C) 2020 openCARP project
5 //
6 // This program is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation, either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program. If not, see <https://www.gnu.org/licenses/>.
18 // ----------------------------------------------------------------------------
19 
20 
21 /*
22  * compute APD of all nodes in an IGB file
23  * or detect an upstroke
24  *
25  * In detection mode, the program exits with status code 3 when an upstroke is detected.
26  * The node and time of the upstroke are output. If no APs are detected, it outputs nothing
27  * and exits with status 0.
28  */
29 #include <iostream>
30 #include <unistd.h>
31 #include <stdlib.h>
32 #include <math.h>
33 #include <fstream>
34 #include <set>
35 #include <vector>
36 #include <deque>
37 #include "IGBheader.h"
38 #include "apd_cmdline.h"
39 
41 
42 using namespace std;
43 
44 const float UNCOMPUTED = -1;
45 const float UNSTARTED = -1;
46 const float FINISHED = -2;
47 const int NO_PRIOR = -1;
48 
51 float
52 lininterp( float y0, float y1, float x0, float x1, float interp )
53 {
54  float m = (y1-y0)/(x1-x0);
55  float b = y1-m*x1;
56 
57  return (interp-b)/m;
58 }
59 
60 
61 void output_apd( FILE *out, int n, float *o1, float *o2=NULL, float o1scale=1 ){
62 
63  if( o2 ) {
64  for( int i=0; i<n; i++ )
65  fprintf( out, "%f\t%f\n", o1[i]*o1scale, o2[i] );
66  } else {
67  for( int i=0; i<n; i++ )
68  fprintf( out, "%f\n", o1[i]*o1scale );
69  }
70 }
71 
72 
73 void process_action_potential(gengetopt_args_info &, IGBheader *, vector<int> &, FILE *, int);
74 void process_unipoles(gengetopt_args_info &, IGBheader *, vector<int> &, FILE *, int);
75 void process_bipoles(gengetopt_args_info &, IGBheader *, vector<int> &, FILE *, int);
76 
93 bool
94 process_specifier( char *sp, set<int> &nodes, int max )
95 {
96  int inc = 1;
97  if( strchr( sp, ':' ) ){
98  char *colptr = strchr( sp, ':' );
99  int consumed;
100  sscanf( colptr+1, "%d%n", &inc, &consumed );
101  if( !consumed || consumed!=strlen(colptr+1) ) {
102  fprintf( stderr, "Illegal range specified: %s\n", sp );
103  exit(1);
104  }
105  *colptr = '\0';
106  }
107 
108  if( !strcmp( "*", sp ) ) {
109  if( inc==1 ) {
110  nodes.clear();
111  for( int i=0; i<max; i+=inc )
112  nodes.insert(nodes.end(),i);
113  } else {
114  for( int i=0; i<max; i+=inc )
115  nodes.insert(i);
116  }
117  return inc==1;
118  }
119 
120  if( strchr( sp, '-' ) ){
121  int s;
122  char fs[100];
123  int nr = sscanf( sp, "%d-%s", &s, fs );
124  if( nr<2 ) {
125  fprintf( stderr, "Illegal range specified: %s\n", sp );
126  exit(1);
127  }
128 
129  int f;
130  if( !strcmp(fs, "*") )
131  f = max-1;
132  else {
133  int consumed;
134  sscanf( fs, "%d%n", &f, &consumed );
135  if( consumed != strlen(fs) ) {
136  fprintf( stderr, "Illegal range specified: %s\n", sp );
137  exit(1);
138  }
139  }
140 
141  if( s<0 || f<s ) {
142  fprintf( stderr, "Illegal range specified: %s\n", sp );
143  exit(1);
144  }
145  if( f>=max || s>=max ) {
146  fprintf( stderr, "Illegal range, maximum node number exceeded: %s\n", sp );
147  exit(1);
148  }
149  for( int i=s; i<=f; i+=inc )
150  nodes.insert( i );
151 
152  } else {
153 
154  int num;
155  sscanf( sp, "%d", &num );
156 
157  if( !sscanf( sp, "%d", &num ) || num<0 || num>=max ) {
158  fprintf( stderr, "Illegal node number specified in list: %d\n", num );
159  exit(1);
160  }
161  nodes.insert( num );
162  }
163  return false;
164 }
165 
166 
167 void
168 extract_nodes( const char *list, vector<int> &nodes, int max )
169 {
170  char *lc = strdup( list );
171  char *sp = strtok( lc, "," );
172  set<int> ch_nodes; // deal with overlapping ranges
173 
174  while( sp != NULL ) {
175 
176  if( process_specifier( sp, ch_nodes, max ) ) break;
177 
178  sp = strtok( NULL, "," );
179  }
180  free( lc );
181 
182  if( !ch_nodes.size() ) {
183  fprintf( stderr, "No nodes specified: %s\n", list );
184  exit(1);
185  }
186 
187  // convert set to vector
188  nodes.resize( ch_nodes.size() );
189  int idx=0;
190  auto end=ch_nodes.end();
191  for( auto sit=ch_nodes.cbegin(); sit!=end; sit++ )
192  nodes[idx++] = *sit;
193 
194 }
195 
196 
197 int
198 main( int argc, char *argv[] )
199 {
200  gengetopt_args_info args;
201 
202  // let's call our cmdline parser
203  if (cmdline_parser (argc, argv, &args) != 0)
204  exit(1);
205 
206  // the last arg must be a file name
207  IGBheader* h;
208  FILE *fin = stdin;
209  try {
210  if( args.inputs_num && strcmp(args.inputs[0],"-") )
211  fin = fopen( args.inputs[0], "r" );
212  h= new IGBheader( fin, true );
213  }
214  catch( int a ) {
215  cerr << "File is not a proper IGB file: "<< args.inputs[0] << endl;
216  exit(1);
217  }
218  int first = 0;
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();
223 
224  FILE *out = stdout;
225  if( strcmp(args.output_file_arg,"-") )
226  out = fopen( args.output_file_arg, "w" );
227 
228  vector<int> nodes;
229  extract_nodes( args.check_nodes_arg, nodes, h->slice_sz() );
230 
231  if( args.signal_arg == signal_arg_AP )
232  process_action_potential( args, h, nodes, out, first );
233  else if( args.signal_arg == signal_arg_Uni )
234  process_unipoles( args, h, nodes, out, first );
235  else if( args.signal_arg == signal_arg_Bip )
236  process_bipoles( args, h, nodes, out, first );
237 
238  fclose( out );
239  exit(0);
240 }
241 
258 void
259 process_bipoles(gengetopt_args_info &args, IGBheader *h, vector<int> &nodes, FILE *out, int first)
260 {
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));
264  vector<float> lat(h->slice_sz(),UNCOMPUTED);
265  vector<float> maxphi(h->slice_sz(),UNCOMPUTED);
266  vector<float> act(h->slice_sz(),UNSTARTED);
267  vector<float> prevlat(h->slice_sz(),-2*args.minapd_arg);
268  vector<deque<float>> actwin(nodes.size());
269 
270  // using centered difference so multiply by 2
271  float DVDT_THRESH = args.dvdt_arg*h->inc_t()*2;
272  if( args.debug_flag ) std::cout << "DVDT_THRESH: " << DVDT_THRESH << std::endl;
273 
274  int unfound = nodes.size();
275 
276  first = std::max( first, 2 );
277 
278  // slices to throw away
279  for( int t=0; t<first-1; t++ )
280  h->read_data( olderphi );
281  h->read_data( oldphi );
282 
283  for( int t=first; t<h->t() && unfound; t++ ){
284 
285  h->read_data( phi );
286 
287 #pragma omp parallel for
288  for( int j=0; j<nodes.size(); j++ ) {
289 
290  int n = nodes[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;
296 
297  if( act[n] == UNSTARTED ) { // check for start
298  if( active && dphi>=DVDT_THRESH ) {
299  act[n] = lat[n] = t-1;
300  maxphi[n] = magphi;
301  if( args.debug_flag ) std::cout << n << " act:" << act[n] << " mag:" <<
302  magphi<<" dphi/dt:" << dphi << std::endl;
303  }
304  } else if( maxphi[n] == FINISHED ) { // only first
305  continue;
306  } else if( active ) { // check for maximal value
307  if( magphi>maxphi[n] ) {
308  maxphi[n] = magphi;
309  lat[n] = t-1;
310  }
311  } else if( !active ) { // end of activation
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 ) { // acceptable activation
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 ) {
319  --unfound;
320  maxphi[n] = FINISHED;
321  if( args.debug_flag ) std::cout << n << " act: " << lat[n] << std::endl;
322  } else {
323  cout << n << " : " << lat[n]+h->org_t() << std::endl;
324  act[n] = UNSTARTED; // restart the search
325  maxphi[n] = UNCOMPUTED;
326  prevlat[n] = lat[n];
327  }
328  } else { // rejected
329  if( args.debug_flag ) std::cout << n << " abandoned: @" << t << " duration:" <<
330  t-1-act[n] << " prev:" << prevlat[n] << std::endl;
331  act[n] = UNSTARTED;
332  maxphi[n] = UNCOMPUTED;
333  }
334  }
335  }
336  auto *tmp = olderphi;
337  olderphi = oldphi;
338  oldphi = phi;
339  phi = tmp;
340  }
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 )
344  output_apd( out, h->slice_sz(), lat.data() );
345 }
346 
347 void
348 process_unipoles(gengetopt_args_info &args, IGBheader *h, vector<int> &nodes, FILE *out, int first)
349 {
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));
353  vector<float> repol(h->slice_sz(),UNCOMPUTED);
354  vector<float> lat(h->slice_sz(),UNCOMPUTED);
355  vector<float> apd(h->slice_sz(),UNCOMPUTED);
356  vector<float> mindphi(h->slice_sz());
357  vector<float> apstart(h->slice_sz(),UNSTARTED);
358 
359  // using centered difference so multiply by 2
360  float DVDT_THRESH = args.dvdt_arg*h->inc_t()*2;
361  float DVDT_THRESH_REPOL = args.dvdt_repol_arg*h->inc_t()*2;
362 
363  int unfound = nodes.size();
364 
365  first = std::max( first, 2 );
366 
367  // slices to throw away
368  for( int t=0; t<first-2; t++ )
369  h->read_data( phi );
370 
371  h->read_data( olderphi );
372  h->read_data( oldphi );
373 
374  for( int t=first; t<h->t() && unfound; t++ ){
375 
376  h->read_data( phi );
377 
378 #pragma omp parallel for
379  for( int j=0; j<nodes.size(); j++ ) {
380 
381  int i = nodes[j];
382  float dphi = phi[i]-olderphi[i];
383 
384  if( apstart[i]==UNSTARTED ) {
385  if( dphi <= DVDT_THRESH ) {
386  apstart[i] = t;
387  mindphi[i] = dphi;
388  lat[i] = t-1;
389  }
390  } else if ( apstart[i] != FINISHED ) { // activation detected
391  if( t-apstart[i]<=args.upstroke_dur_arg && dphi<mindphi[i] ) {
392  mindphi[i] = dphi;
393  lat[i] = t-1;
394  } else if( t-apstart[i]>args.maxapd_arg ) { // print what you have
395  if( repol[i] != UNCOMPUTED ) apd[i] = repol[i] - lat[i];
396  if( args.mode_arg == mode_arg_first ) {
397  --unfound;
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();
402  cout << endl;
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();
407  cout << endl;
408  } else if( args.mode_arg == mode_arg_repol_first ) {
409  if( repol[i] != UNCOMPUTED ) {
410  repol[i] += h->org_t();
411  }
412  } else if( args.mode_arg == args.debug_flag ) {
413  cout << "node:" << i << "\tAPD: " << apd[i] << "\tstarted: " << lat[i] + h->org_t();
414  cout << endl;
415  }
416  if( (args.mode_arg==mode_arg_all || args.debug_flag ||
417  args.mode_arg==mode_arg_repol_all ) ){
418  apstart[i] = UNSTARTED;
419  repol[i] = UNCOMPUTED;
420  } else {
421  apstart[i] = FINISHED;
422  }
423  } else if( t-apstart[i]>args.minapd_arg ) { // look for repolarization
424  if( dphi>=DVDT_THRESH_REPOL &&
425  (repol[i]==UNCOMPUTED || dphi>mindphi[i]) ) {
426  repol[i] = t-1;
427  mindphi[i] = dphi;
428  }
429  }
430  }
431  }
432  float *tmp = olderphi;
433  olderphi = oldphi;
434  oldphi = phi;
435  phi = tmp;
436  }
437  for( int i=0; i<lat.size(); i++ ) lat[i]+=h->org_t();
438 }
439 
440 
441 void
442 process_action_potential(gengetopt_args_info &args, IGBheader *h, vector<int> &nodes, FILE *out, int first)
443 {
444  float VmREST = -80; //default value
445  float DVDT_THRESH = args.dvdt_arg*h->inc_t();
446  float repolarize = args.repol_arg; // %repolarization
447  float v_thresh = args.rep_level_arg;
448  float plusthresh = 10.;
449 
450  thresh_t threshtype;
451  if( args.threshold_mode_counter )
452  threshtype = Fixed;
453  else
454  threshtype = PerCentRepol;
455 
456  int MINAPD = args.minapd_arg; // min APD
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;
461  }
462 
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.);
472  vector<float> apdstart( h->slice_sz(),UNSTARTED);
473 
474  int *last_apstart = NULL;
475  if( args.mode_arg==mode_arg_act) last_apstart = (int *)calloc(h->slice_sz(),sizeof(int));
476 
477  for( int i=0; i<h->slice_sz(); i++ ) {
478  apd[i] = UNCOMPUTED;
479  peaked[i] = false;
480  vmrest[i] = VmREST;
481  vmthresh[i] = threshtype==MinDeriv?0:v_thresh;
482  if( args.mode_arg==mode_arg_act) last_apstart[i] = NO_PRIOR;
483  }
484 
485  int unfound = nodes.size();
486 
487  for( int t=0; t<first; t++ )
488  h->read_data( oldvm );
489 
490  for( int t=first; t<h->t() && unfound; t++ ){
491 
492  h->read_data( vm );
493 
494  if( !t )
495  memcpy( oldvm, vm, h->slice_sz()*sizeof(float) );
496 
497 #pragma omp parallel for
498  for( int j=0; j<nodes.size(); j++ ) {
499 
500  int i = nodes[j];
501 
502  if( apdstart[i] == UNSTARTED ) {
503 
504  // find resting level
505  if( t && fabs(vm[i]-oldervm[i])<0.001 )
506  vmrest[i] = oldvm[i];
507 
508  if( (vm[i]-oldvm[i])>DVDT_THRESH && vm[i]>args.vup_arg ) { // AP start found
509 
510  if( oldvm[i]<=args.vup_arg )
511  apdstart[i] = lininterp( oldvm[i], vm[i], t-1, t, args.vup_arg )*h->inc_t();
512  else {
513  apdstart[i] = lininterp( oldvm[i]-oldervm[i], vm[i]-oldvm[i],
514  t-1, t, DVDT_THRESH )*h->inc_t();
515  }
516 
517  if( args.mode_arg == mode_arg_detect ) {
518  cout << i << " @ " << apdstart[i]+h->org_t() << endl;
519  exit(3);
520  }
521  }
522  }
523 
524  // find peak Vm for repolarization
525  if( !peaked[i] && apdstart[i]!=UNSTARTED ) {
526  switch( threshtype ) {
527  case Fixed:
528  if( vm[i]<oldvm[i] )
529  peaked[i] = true;
530  break;
531  case AboveRest:
532  if( vm[i]<oldvm[i] ) {
533  peaked[i] = true;
534  vmthresh[i] = vmrest[i]+plusthresh;
535  }
536  break;
537  case PerCentRepol:
538  if( args.peak_value_arg == peak_value_arg_upstroke ) { // find peak upstoke
539  if( vm[i]<oldvm[i] ) {
540  peaked[i] = true;
541  vmthresh[i] = vmrest[i] + (1.-repolarize/100.)*(oldvm[i] -vmrest[i] );
542  }
543  } else { // find plateau peak
544  if( t*h->inc_t()-apdstart[i] < args.plateau_start_arg ) {
545  vmthresh[i] = vm[i];
546  } else if( t*h->inc_t()-apdstart[i] < args.plateau_start_arg+args.plateau_duration_arg ) {
547  if( vm[i] > vmthresh[i] )
548  vmthresh[i] = vm[i];
549  } else {
550  vmthresh[i] = vmrest[i] + (1.-repolarize/100.)*(vmthresh[i] -vmrest[i] );
551  peaked[i] = true;
552  }
553  }
554  break;
555  default: // do nothing
556  break;
557  }
558  // ensure the minimum peak upstroke was met
559  if( peaked[i] && vm[i]-vmrest[i]<args.mindv_arg ) {
560  apdstart[i] = UNSTARTED;
561  peaked[i] = false;
562  if( args.debug_flag )
563  cout << "node: " << i << " failed to reach min dv/dt at frame " << t<< endl;
564  }
565  }
566 
567  if( peaked[i] && apd[i]==UNCOMPUTED && args.mode_arg==mode_arg_act_first ) {
568  unfound--;
569  } else if( peaked[i] && apd[i]==UNCOMPUTED && args.mode_arg==mode_arg_act) {
570  // look at activations
571  if( last_apstart[i]==NO_PRIOR || (t-last_apstart[i])*h->inc_t()>MINAPD ) {
572  cout << i << " : " << apdstart[i]+h->org_t() << endl;
573  last_apstart[i] = t;
574  }
575  apdstart[i] = UNSTARTED;
576  peaked[i] = false;
577  } else if( peaked[i] && apd[i]==UNCOMPUTED ) {
578  if( args.integral_flag ) integral[i] += vm[i]-vmrest[i];
579  // look fpr AP end
580  bool ended=false;
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];
585  ended = true;
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];
588  ended = true;
589  }
590  if( ended ) {
591  if( apd[i] < MINAPD ) { // too short so throw it away and keep looking
592  apdstart[i] = UNSTARTED;
593  peaked[i] = false;
594  apd[i] = UNCOMPUTED;
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 ) {
599  --unfound;
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();
604  cout << endl;
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();
609  cout << endl;
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();
613  }
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];
618  cout << endl;
619  }
620  }
621  }
622  // reset if looking for more APs
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 ) {
626  apdstart[i] = UNSTARTED;
627  peaked[i] = false;
628  if( args.integral_flag ) integral[i] = 0.;
629  apd[i] = UNCOMPUTED;
630 
631  }
632  }
633  float *tmp = oldervm;
634  oldervm = oldvm;
635  oldvm = vm;
636  vm = tmp;
637  }
638 
639  for( int i=0; i<apdstart.size(); i++ ) apdstart[i]+=h->org_t();
640 
641  if( args.mode_arg == mode_arg_first ) {
642  output_apd( out, h->slice_sz(),
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 ) {
649  output_apd( out, h->slice_sz(), apdstart.data() );
650  }
651 }
void extract_nodes(const char *list, vector< int > &nodes, int max)
Definition: IGBapd.cc:168
int main(int argc, char *argv[])
Definition: IGBapd.cc:198
thresh_t
Definition: IGBapd.cc:40
@ MinDeriv
Definition: IGBapd.cc:40
@ PerCentRepol
Definition: IGBapd.cc:40
@ Fixed
Definition: IGBapd.cc:40
@ AboveRest
Definition: IGBapd.cc:40
void process_unipoles(gengetopt_args_info &, IGBheader *, vector< int > &, FILE *, int)
Definition: IGBapd.cc:348
const float FINISHED
Definition: IGBapd.cc:46
const int NO_PRIOR
Definition: IGBapd.cc:47
void process_action_potential(gengetopt_args_info &, IGBheader *, vector< int > &, FILE *, int)
Definition: IGBapd.cc:442
float lininterp(float y0, float y1, float x0, float x1, float interp)
Definition: IGBapd.cc:52
void process_bipoles(gengetopt_args_info &, IGBheader *, vector< int > &, FILE *, int)
determine activations in bipolar recordings
Definition: IGBapd.cc:259
const float UNSTARTED
Definition: IGBapd.cc:45
bool process_specifier(char *sp, set< int > &nodes, int max)
Definition: IGBapd.cc:94
void output_apd(FILE *out, int n, float *o1, float *o2=NULL, float o1scale=1)
Definition: IGBapd.cc:61
const float UNCOMPUTED
Definition: IGBapd.cc:44
float org_t(void)
Definition: IGBheader.h:317
size_t t(void)
Definition: IGBheader.h:277
int read_data(T *dp, size_t numt=1, char *buf=NULL)
Definition: IGBheader.h:433
size_t slice_sz()
Definition: IGBheader.h:263
void inc_t(float a)
Definition: IGBheader.h:329
constexpr T max(T a, T b)
Definition: ion_type.h:31