TITLE PoissonBinauralVM.mod  Binaural Inhomogenous Poisson: 
 : binaural von Mises probability distributions

COMMENT
 Written by Jonathan Z Simon, jzsimon@isr.umd.edu
 A binaual inhomogenous poisson process
 Can be tailored to use many different distributions as stimuli.
 Stimulus Specific sections highlighted for makers of new stimuli 
 :------------------STIMULUS SPECIFIC Description --------------------------
  Uses 2 von Mises distributions (see e.g. Fisher, NI, Statistical Analysis
  of Circular Data), differing only in phase between ipsi and contra.
 :--------------------------------------------------------------------------
ENDCOMMENT

NEURON {

 :------------------STIMULUS SPECIFIC POINT_PROCESS name -------------------
   POINT_PROCESS PoissonBinauralVM
 :--------------------------------------------------------------------------

 RANGE 

 :------------------STIMULUS SPECIFIC RANGE variables ----------------------
  : PARAMETERs
   vs, freq, stimPhaseIpsi, stimPhaseContra, stimRate,
   genericParam1, genericParam2,
  : ASSIGNEDs
   angfreq, stimProb, kappa, norm, stim,
 :--------------------------------------------------------------------------
 
  poissDt, tickSize, tickVal, tickOnce : Stimulus independent
 POINTER probIpsi, probContra
}

UNITS {

 :------------------STIMULUS SPECIFIC UNITS --------------------------------
  (kHz) = (kiloHz)
 :--------------------------------------------------------------------------

 PI = (pi) (1)
 (tick) = (1)
 (prob) = (1)
 (angle) = (1) :should be radians but those are defined as .5 / pi
}

PARAMETER {

 :------------------STIMULUS SPECIFIC PARAMETERs ---------------------------
  vs = 0.43 (1)               <0,0.999> :otherwise too peaked even for exp()
  freq = 1000 (Hz)
  stimPhaseIpsi = 0 (angle)
  stimPhaseContra = 0 (angle)
  stimRate = 3.46 (/ms)       <1e-9,1e9>
  genericParam1 = 0 : unused
  genericParam2 = 0 : unused
 :--------------------------------------------------------------------------

 poissDt = 0.025 (ms)        <1e-9,1e9>
 tickSize = 1 (tick)         <1e-9,1e9>
}

ASSIGNED {
 :------------------STIMULUS SPECIFIC ASSIGNEDs ----------------------------
  angfreq (/ms)
  stimProb (prob)
  kappa (1)
  norm (1)
 :--------------------------------------------------------------------------

 tickVal (tick)
 tickOnce (tick)
 stim[2]  (prob) : [0] is Ipsi & [1] is contra
 probIpsi (prob)
 probContra (prob)

}

INITIAL {

 :------------------STIMULUS SPECIFIC INITIALizations ----------------------
  angfreq = 2*PI*freq*(0.001) : (0.001) from Hz & ms
  stimProb = stimRate*poissDt
  if (vs < 0) {vs = 0} 
  if (vs >= 0.999) {vs = 0.999} :otherwise too peaked even for exp()
  kappa =  A1Inv(vs)
  norm = 1/(2*PI*IO(kappa))
 :--------------------------------------------------------------------------

 tickVal = 0
 tickOnce = 0

 stim[0] = calcStimIpsi()
 stim[1] = calcStimContra()
 if (pointersValid()) {
  probIpsi = stimProb*stim[0]
  probContra = stimProb*stim[1]  
 }
}

:------------------STIMULUS SPECIFIC Stimulus Functions -------------------
 
 FUNCTION calcStimIpsi() {

  :von Mises probablility distribution (phase = 2 pi freq t + phiIpsi)

  calcStimIpsi = norm*exp(kappa*sin(angfreq*t+stimPhaseIpsi))

 }
 
 FUNCTION calcStimContra() {

  :von Mises probablility distribution (phase = 2 pi freq t + phiContra)

  calcStimContra = norm*exp(kappa*sin(angfreq*t+stimPhaseContra))

 }
 
:--------------------------------------------------------------------------

:------------------STIMULUS SPECIFIC Misc. Functions & Procedures----------
 FUNCTION IO(x) { 
 : Bessel Function I_O(x)
 : approximation from Fisher, NI, Statistical Analysis of Circular Data
  LOCAL w, w2, w4, w6, w8 : local vars for computational speed only
  w = x/3.75
  w2 = w*w
  w4 = w2*w2
  w6 = w4*w2
  w8 = w4*w4
  if (w < 1) {
   IO = (1 + 3.5156229*w2 + 3.0899424*w4 + 1.2067492*w6 + 0.2659732*w8 + 
             0.0360768*w8*w2 + 0.0045813*w8*w4)
  } else {
   IO = (0.39894228 + 0.01328592/w + 0.00225319/w2 - 0.00157565/(w2*w) + 
         0.00916281/w4 - 0.02057706/(w4*w) + 0.02635537/w6 - 
         0.01647633/(w6*w) + 0.00392377/w8)*exp(x)/sqrt(x)
  }
 }
 
 FUNCTION A1Inv(x) { 
 : Bessel Function Ratio: A_1(x) = I_1(x)/I_O(x), and use Inverse
 : approximation from Fisher, NI, Statistical Analysis of Circular Data
  if (x < 0.53) {
   A1Inv = (2*x + x*x*x + 5/6*x*x*x*x*x)
  } else { 
   if (x < 0.85) {
    A1Inv = (-0.4 + 1.39*x + 0.43/(1-x))
   } else {
    A1Inv = (1/(x*x*x - 4*x*x + 3*x))
   }
  }
 }

:--------------------------------------------------------------------------

: Inelegant but necessary to get poisson "clock" started.
: Must be called once after finitialize() but before run
PROCEDURE prerun() {
 tickVal  = tickSize
 tickOnce = tickSize
}

NET_RECEIVE (weight) {
 if(tickVal) {
  tickVal = 0
 } else {
  tickVal = tickSize
  stim[0] = calcStimIpsi()
  stim[1] = calcStimContra()
  probIpsi   = stimProb*stim[0]
  probContra = stimProb*stim[1]
 }
 net_send(poissDt,1)
}

FUNCTION pointersValid() {
 if ((!nrn_pointing(probIpsi))||(!nrn_pointing(probContra))) {
  printf(
   "Poisson Process: probIpsi and probContra must be set using setpointer!\n")
  pointersValid = 0
 } else {
  pointersValid = 1
 }
}