/*
Encapsulates a NetStim and an SGate artificial spiking cell connected like this
  NS--< SGate
so that the SGate's output is a spike train whose "instantaneous mean frequency"
is modulated by a sinusoid of the form
1 + depth*(cos(2*PI*(t-start)/period) - 1)/2
Thus the mean spike frequency varies between a maximum of fmax = 1/ns.interval
and a minimum of fmin = fmax*(1-depth).

If you need statistically independent spike streams, be sure to pair
the NetStim and the SGate with separate instances of the Random class
as per the comments in their NMODL source code.

Remark to myself:  consider using an FInitializeHandler 
to turn the NetStim on & off.

NTC 5/2/2012
*/

begintemplate sintrain
	public pp // this is the thing that should be the spike source for NetCons
	// associate this with a Random.uniform(0,1) instance for statistical independence
	public ns // to make it easy to associate the NetStim with its own Random.negexp(1)
	public fmax // arg is in Hz!
	public noise, start, period, number, depth, phase
	public connect_pre, is_art, randi, gid, noiseFromRandomPP, noiseFromRandomNS, setnoiseFromRandomPP, setnoiseFromRandomNS
	public x, y, z, position, xpos, ypos, zpos

  objref ns, pp, nc

  proc init() {
 		gid = $1
		randi = $2

	ns = new MyNetStim(.5)
    pp = new SGate()
    nc = new NetCon(ns, pp)
    nc.delay = 0

    ns.number = 1e9
    fmax(1000)
    noise(1)
    start(0)

    period(150)
    number(1)
    depth(0.75)
    phase(0)
	
	ns.gid = $1
	ns.randi = $2
	
	pp.gid = $1
	pp.randi = $2
  }

  // the following are to be used for setting/getting param values

  func fmax() { local retval
    if (numarg()==1) {
      retval = $1
      if (retval<=0) {
        print retval, " out of range for high frequency"
        retval = -1 // return -1 to signal an error
      } else {
        ns.interval = 1000/retval
      }
    } else {
     retval = 1000/ns.interval
    }
    return retval
  }

  func noise() {
    if (numarg()==1) {
      retval = $1
      if ((retval<0) || (retval>1)) {
        print retval, " out of range for noise"
        retval = -1
      } else {
        ns.noise = retval
      }
    } else {
      retval = ns.noise
    }
    return retval
  }

  func start() {
    if (numarg()==1) {
      retval = $1
      if (retval<0) {
        print retval, " out of range for start time"
        retval = -1
      } else {
        ns.start = retval // no point having ns generate anything before it is needed
        pp.start = retval
      }
    } else {
      retval = ns.start
    }
    return retval
  }

  func period() {
    if (numarg()==1) {
      retval = $1
      if (retval<=0) {
        print retval, " out of range for modulation period"
        retval = -1 // return -1 to signal an error
      } else {
        pp.period = retval
      }
    } else {
      retval = pp.period
    }
    return retval
  }

  func phase() {
    if (numarg()==1) {
      retval = $1
      if (retval<0) {
        print retval, " phase less than 0"
        pp.phase = 0
      } else {
        pp.phase = retval
      }
    } else {
      retval = pp.phase
    }
    return retval
  }

  func number() {
    if (numarg()==1) {
      retval = $1
      if (retval<=0) {
        print retval, " out of range for number of modulation periods"
        retval = -1 // return -1 to signal an error
      } else {
        pp.number = retval
      }
    } else {
      retval = pp.number
    }
    return retval
  }

  func depth() {
    if (numarg()==1) {
      retval = $1
      if ((retval<0) || (retval>1)) {
        print retval, " out of range for modulation depth"
        retval = -1 // return -1 to signal an error
      } else {
        pp.depth = retval
      }
    } else {
      retval = pp.depth
    }
    return retval
  }

  // these last three are cribbed from Network Builder hoc code--
  // they'd be part of any template based on an artificial spiking cell


  obfunc connect_pre() { localobj nc
    nc = new NetCon(pp, $o1)
    if (numarg() == 2) { $o2 = nc }
    return nc
  }

	func is_art() {return 1}
	

	proc position(){
		x = $1  y = $2  z = $3	
		xpos = $1  ypos = $2  zpos = $3	
		ns.position(xpos, ypos, zpos)
	}
 
  proc setnoiseFromRandomPP() {
  	pp.noiseFromRandom($o1) // pass an instance of the Random class that is set to the Random.uniform(0,1) distribution for the gate
  }
  proc setnoiseFromRandomNS() {
  	ns.noiseFromRandom($o1) // pass an instance of the Random class that is set to the Random.negexp(1) distribution for the poisson spike train
  }

endtemplate sintrain