RR Interval Time Series Modeling: The PhysioNet/Computing in Cardiology Challenge 2002 1.0.0

File: <base>/generators/rrgen-171.c (15,499 bytes)
/* file: rrgen.c
     Entry number  : 171
     Authors       : Dragan Gamberger, Ivan Maric, Tomislav Smuc,
                      Gordan Bosanac, Nikola Bogunovic, Goran Krstacic
     Organization  : Rudjer Boskovic Institute, 
		     Institute for Cardiovascular Prevention and Rehabilitation
                     Zagreb, Croatia
     email address : dragan.gamberger at irb.hr

   This program should compile without errors or warnings using:
	gcc -Wall rrgen.c -lm

   See http://www.physionet.org/challenge/2002/ for further information on
   the CinC Challenge 2002.

   This program was used to generate series rr27 and rr43 of the challenge
   dataset.
*/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>


#define	SPS	128	/* sampling frequency (determines quantization of
			   RR intervals;  see main()) */

#define     STACK  20	/* stack daepth for mean value computation */
#define     def_load  0.35	/* default load during the day */

float val[STACK];		/* previous interval values stack*/
float ip[STACK];		/* integral power values stack */
float val_mean;		/* mean value of interval values in stack */
float ip_mean;		/* mean value of integral power in stack */
float rr_high;		/* expected interval value for load 0.0*/
float rr_low;		/* expected interval value for load 1.0*/
float abs_max;		/* absolut maximal interval value */
float abs_min;		/* absolut minimal interval value */
float heart_load;		/* present heart load 0.0 - 1.0 */
float int_pow;		/* integral power (needed by the body) */
float int_pow_change;	/* change of power */
float mem_prev;		/* previous disorder */
int aux_counter;		/* auxilliary counter */

		/* load variables */
float load0;		/* long term load */
float load00;		/* intended long term load */

float load1;		/* permanent load, steps */
int load1c;			/* permanent load counter */

float load2;		/* permanent load, continuous */
float load2p;		/* positive step */
float load2m;		/* negative step */
int load2cp;		/* positive step counter */
int load2cm;		/* negative step counter */

float load3;		/* long term load, continuous */
float load3p;		/* positive step */
float load3m;		/* negative step */
int load3cp;		/* positive step counter */
int load3cm;		/* negative step counter */

float load4;		/* medium term load, continuous */
float load4p;		/* positive step */
float load4m;		/* negative step */
int load4cp;		/* positive step counter */
int load4cm;		/* negative step counter */

float load5;		/* medium term load, steps */
int load5c;			/* counter */

float load6;		/* short term load, steps */
int load6c;			/* counter */


	/* global time constants */
float start_ss,per_ss1,per_ss2;	/* basic sleep period */
float incr1,incr2;			/* increments for sleep */
float start_wk1,per_wk1;		/* basic work period */
float start_wk2,per_wk2;		/* intensive work period */

	/* main random generating routine */
float randn(void)
{
   return(((float)rand())/((float)RAND_MAX));
}

float distri(xa,xbl,xbh,xcl,xch)
float xa,xbl,xbh,xcl,xch;
{
   float x,y;
   double base,exp,res;
   x=randn();
   if(x>.5) {			/* increase interval */
      x=2.0*(x-0.5);
      exp=x;			/* x = 0,1 */
      base=2.0;			/* select distribution slope 2=flat, 25 steep */
      if(xch/xbh > 2.65) base=4.0;
      if(xch/xbh > 3.30) base=8.0;
      if(xch/xbh > 4.10) base=12.0;
      if(xch/xbh > 4.70) base=16.0;
      if(xch/xbh > 5.50) base=25.0;
      res=pow(base,exp);
      y=xa+xch*((float) res -1.0)/((float) base -1.0);
   }
   else {				/* decrease interval */
      exp=2.0*x;			/* x = 0,1 */
      base=2.0;			/* select distribution slope 2=flat, 25 steep */
      if(xcl/xbl > 2.65) base=4.0;	/* both xbl xcl MUST be negative */
      if(xcl/xbl > 3.30) base=8.0;
      if(xcl/xbl > 4.10) base=12.0;
      if(xcl/xbl > 4.70) base=16.0;
      if(xcl/xbl > 5.50) base=25.0;
      res=pow(base,exp);
      y=xa+xcl*((float) res -1.0)/((float) base -1.0);	/* xcl is negative */
   }
   return(y);
}

/* This function is called once, before generate() is called.  See main()
   for a description of its arguments.
*/

void initialize(long seed, long tmax)
{
    int j,jj;					/* auxilliary counters */

    srand((unsigned int)seed);		/* initialize random generator */

    for (j=0;;) {
       j =(int) (randn()*3000) -2000;	/* do not use few first random numbers */
       if(j>0) break;
    }
    for(jj=0;jj<j+10;jj++) randn();	
    			
		/* select and compute constants */
    rr_low = 0.48 + 0.05*randn();		/* select rr at maximal load */
    rr_high = rr_low * 1.8 + .05*randn();  /* select rr at minimal load */
    for(;;) {					/* prevent this rr to be too long */
       if(rr_high<1.150) break;
       rr_high-=0.050;
    }
    abs_min = rr_low * .7;  			/* select rr at minimal load */
    for(;;) {					/* prevent this rr to be too short */
       if(abs_min>0.35 && abs_min<rr_low-.05) break;
       if(abs_min<0.35) abs_min+=.015;
       else abs_min-=.015;
    }
    abs_max = rr_high * 1.52;			/* select theoretically maximal rr */
    for(;;) {					/* prevent this rr to be too long */
       if(abs_max<1.550) break;
       abs_max-=.05;
    }
			/* initialize previous rr intervals */
    for(j=0;j<STACK;j++) val[j]=(rr_low+rr_high)/2.0;
    for(j=0;j<STACK;j++) ip[j]=0.0;
    val_mean=(rr_low+rr_high)/2.0;
    ip_mean=0.0;

    int_pow=0;				/* integral power is in stable position */
    load0=def_load;			/* long term load default value*/
    load1=0;				/* permanent load, steps */
    load1c=0;				/* counter */
    load2=0;				/* permanent load, continuous */
    load2cp=0;				/* positive counter */
    load2cm=0;				/* negative counter */
    load3=0;				/* long term load, continuous */
    load3cp=0;				/* positive counter */
    load3cm=0;				/* negative counter */
    load4=0;				/* medium term load, continuous */
    load4cp=0;				/* positive counter */
    load4cm=0;				/* negative counter */
    load5=0;				/* medium term load, steps */
    load5c=0;				/* counter */
    load6=0;				/* short term load, steps */
    load6c=0;				/* counter */
    mem_prev=0.0;				/* no previous memory */

			/* set global time constants */
    start_ss=45000+30000*randn();		/* go to sleep whenever you like */
    per_ss2=20000+10000*randn();		/* duration about 7 hours */
							/* ajust so that you wake at least 1 hour before end */
    if(start_ss + per_ss2 >82000) start_ss = 82000- per_ss2;

    per_ss1=per_ss2*(0.7+0.2*randn());	/* duration of the first sleep part */
    per_ss2 -= per_ss1;				/* duratio of the second part */
    incr1 = (def_load*100)/ per_ss1;
    incr2 = (def_load*100)/ per_ss2;
    start_wk1=7000+1000*randn();		/* start increased load during the day */
    per_wk1=start_ss-start_wk1-10000-1000*randn();
    start_wk2=start_wk1+10000+1000*randn();	/* start second increased load during the day */
    per_wk2=start_wk1+per_wk1-start_wk2-10000-1000*randn();

    aux_counter=0;
    return;
}


/* This function is called once per RR interval.  It should return the length
   of the next (simulated) RR interval in seconds.

   The example code generates samples of a noisy sine wave.
*/

float generate(void)
{
    float rr;				/* new interval value */
    static float t;

    float xa,xbl,xbh,xcl,xch;		/* distribution constants */

    int j;					/* auxilliary counter */
    float tt;				/* auxilliary time */
    float corr1,corr2;			/* correction values for rr*/
    float change;				/* statistical instabilities of rr */

			/* compute load */
				/* long term */
    tt=t;
    for(;;) {						/* ajust for more than 24 hours */
       if(tt<=86400) break;
       else tt-=86400;
    }
 		/* medium load is default value during the day*/
    if(tt<start_ss || tt>start_ss+per_ss1+per_ss2) load0=def_load + 0.05*sin(tt*7.26/100000);	
    else {
       if(aux_counter>0) aux_counter--;
       else {
          aux_counter=100;
          if(tt>start_ss && tt<start_ss+per_ss1) load0-=incr1*2.0*randn();		/* first sleep part */
          if(tt>start_ss+per_ss1 && tt<start_ss+per_ss1+per_ss2) load0+=incr2*2.0*randn();		/* second sleep part */
          if(load0<0) load0=0;
          if(load0>def_load) load0=def_load + 0.05*sin(tt*7.26/100000);
       } 
    }
    if(tt>start_wk1 && tt<start_wk1+per_wk1) load0+=0.1;  
    if(tt>start_wk2 && tt<start_wk2+per_wk2) load0+=0.1;  
    if(tt>start_ss-10000 && tt<start_ss-3000) load0-=0.1;  

	/* permanent load changes, steps */
    if(load1c>0) load1c--;
    if(load1c==0) {
       load1c= (int) (1000*randn());		/* duration of the new offset */
       if(load0<def_load-0.05) load1=0.1*randn();		/* its magnitude */
       else load1=0.1*randn();		
    }
	/* permanent load changes, continuous */
    if(load2cp>0) {					/* first increase load */
       load2-=load2p;
       load2cp--;
    }
    else if(load2cm>0) {				/* after that decrese it */
       load2+=load2m;
       load2cm--;
    }
    if(load2cp==0 && load2cm==0) {			/* determine new period */
       load2=0;
       load2cp= (int) (10+100*randn());		/* up and down times */
       load2cm= (int) (load2cp*randn());
       load2cp -= load2cm;
       if(load0<def_load-0.05) load2p= 0.1*randn();		/* peak value */
       else load2p= 0.1*randn();
       load2m= load2p/((float) (load2cm));	/* decrease value */
       load2p= load2p/((float) (load2cp));	/* increase value */
    }

	/* long term load during the work, continuous */
    if(load3cp>0) {					/* first increase load */
       load3-=load3p;
       load3cp--;
    }
    else if(load3cm>0) {				/* after that decrease it */
       load3+=load3m;
       load3cm--;
    }
    if(load3cp==0 && load3cm==0) {			/* determine new period */
       load3=0;
       if(load0>def_load-0.05 && randn()<0.3) if(randn()<0.001) {
          load3=0.1;
          load3cp= (int) (4500+4000*randn());	/* total up and down times */
          load3cm= (int) (load3cp*randn());     /* decrease time */
          load3cp -= load3cm;				/* increase time */
          load3p= 0.1*randn()+0.1;			/* peak value */
          load3m= load3p/((float) (load3cm));	/* decrease value */
          load3p= load3p/((float) (load3cp));	/* increase value */
       }
    }

	/* medium term load during the work, continuous */
    if(load4cp>0) {					/* first increase load */
       load4-=load4p;
       load4cp--;
    }
    else if(load4cm>0) {				/* after that decrese it */
       load4+=load4m;
       load4cm--;
    }
    if(load4cp==0 && load4cm==0) {			/* determine new period */
       load4=0;
       if(load0>def_load-0.05 && randn()<0.1) if(randn()<0.001) {
          load4=0.1;
          load4cp= (int) (4500+4000*randn());	/* total up and down times */
          load4cm= (int) (load4cp*randn());     /* decrease time */
          load4cp -= load4cm;				/* increase time */
          load4p= 0.1*randn()+ 0.1;			/* peak value */
          if(randn()<0.1) load4p=load4p*(-2.0);
          if(load0+load1+load2+load3>0.7) load4p= 0.1*randn();
          load4m= load4p/((float) (load4cm));	/* decrease value */
          load4p= load4p/((float) (load4cp));	/* increase value */
       }
    }

	/* medium term load during the whole day, steps*/
    if(load5c>0) {
       load5c--;
    }
    else{
       load5=0.0;
       if(randn()<0.0005*(load0+load1+load2+load3+load4+0.1)) {
          load5c= (int) (2500+5000*randn());
          load5=0.15*randn()+0.1;
          if(load0+load1+load2+load3+load4>0.7) load5=load5/4.0;
          if(load0<def_load-0.05) load5=load5/2.0;
       }
    }

	/* short term load during the whole day, steps*/
    if(load6c>0) {
       load6c--;
    }
    else{
       load6=0.0;
       if(randn()<0.005*(load0+load1+load2+load3+load4+load5+0.1)) {
          load6c= (int) (10+50*randn());
          load6=0.1*randn()+0.05;
          if(load0+load1+load2+load3+load4+load5>0.9) load6=load6/4;
          if(val[0]<0.5) load6=0.0;
       }
    }

   

      
    heart_load=load0+load1+load2+load3+load4+load5+load6;		/* total heart load */
    if(heart_load>1.0) heart_load=1.0;			/* normalization */
    if(heart_load<0.0) heart_load=0.0;

			/* compute changes in integral power of the body */

    int_pow_change=heart_load+1.0-2*(rr_high-rr_low)/(val[0]+rr_high-2*rr_low);

    	/* integral power correction, long term stability */
      /* ** increse second constant if global oscilations are too high */ 
      /* maximal oscilation depression with C=0.015 */
    if(int_pow>0) corr1=-.002;
    else corr1=.002;
    if(int_pow>0 && int_pow>ip_mean && int_pow_change>0) corr1-=0.015*randn();
    if(int_pow<0 && int_pow<ip_mean && int_pow_change<0) corr1+=0.015*randn();
    if(int_pow>ip_mean && int_pow_change>0) corr1-=0.015*randn();
    if(int_pow<ip_mean && int_pow_change<0) corr1+=0.015*randn();
    if(int_pow_change>0) corr1-=0.003;
    if(int_pow_change<0) corr1+=0.003;


      /* recent history correction, use it only in stable situation */
    corr2=0;
    if(int_pow>-0.25 && int_pow<0.25 ) {	/* stable condition */	
       corr2+=(val_mean-val[0])*(0.0+1.0*randn())*1.2*val[0];       
       corr2+=(val_mean-val[1])*(0.0+1.0*randn())*1.4*val[0]; 

    }

		/* introduce instabilities */
		/* basic algorithm, use distribution to determine rr changes */
    xa=0.000;
    xbl=-0.010*val[0];
    xbh=0.010*val[0];
    xcl=-0.020*(1.0+val[0]);
    xch=0.030*(1.0+val[0]);
    change=distri(xa,xbl,xbh,xcl,xch);		/* use distribution to compute difference */

	/* asimetric instabilities */
    if(randn()<0.005 && val[0]>0.7)  change+=0.4*randn()*val[0];  

	/*predicted interval value is previous + corrections + instabilities */
    rr=val[0]+corr1+corr2+change;	

	/* suprises */
    if(mem_prev>0.001 || mem_prev<-0.001) {
       if(randn()<0.5) {
          rr-=mem_prev*(0.5+0.5*randn());
          mem_prev=0.0;
       }
    }
    else if(randn() < 0.002) {
       if(val[0]> 0.8 && randn()<0.1) mem_prev=(-1.0)*(0.1+0.1*randn());
       else{
          if(val[0]<1.0 && val[0]>0.6 && randn()<0.5) rr+=.2;
          else mem_prev=0.05+.1*randn();
       }
       rr+=mem_prev;
    }

			/* prevent this rr to be too short or too long*/

    for(;;) {					/* prevent this rr to be too short */
       if(rr>abs_min) break;
       if(int_pow>1.0) rr+=0.05*randn();
       else rr+=0.25*randn();
    }
    for(;;) {					/* prevent this rr to be too long */
       if(rr<abs_max) break;
       rr-=0.5*randn();
    }

    int_pow+=(int_pow_change);		/* update integral power */

    val_mean+=(rr-val[9])/10.0;		/* compute means */
    ip_mean+=(int_pow-ip[9])/10.0;
							/* save in stack */
    for(j=STACK-1;j>0;j--) val[j]=val[j-1];
    val[0]=rr;
    for(j=STACK-1;j>0;j--) ip[j]=ip[j-1];
    ip[0]=int_pow;

    t += rr;
    return (rr);
}
    
int main(int argc, char **argv)
{
   float t = 0.0;	    /* sum of intervals since the beginning of the
			       simulation, in seconds */
    long ts = 0;	    /* t, in sample intervals */
    long tsp = 0;	    /* previous value of ts */
    long tmax = 24*60*60;   /* 24 hours, in seconds */
    long seed;		    /* a 32-bit random variable that can be used to
			       initialize the generator */
    long atol();

    if (argc < 2) {
	fprintf(stderr, "usage: %s seed [tmax]\n", argv[0]);
	exit(1);
    }
    seed = atol(argv[1]);
    if (argc > 2)
	tmax = atol(argv[2]);

    initialize(seed, tmax);
    while ((t += generate()) < tmax) {	/* add RR interval to running time */
	/* calculate and output a quantized RR interval */
	ts = (long)(SPS*t + 0.5);
	printf("%5.3f\n", (ts - tsp)/((float)SPS));
	tsp = ts;
    }

    exit(0);
}