/* 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