// $Id: arm.hoc,v 1.134 2012/03/08 17:56:22 samn Exp $ DOPE_INTF6 = 1 EDOPE_INTF6 = 1 IDOPE_INTF6 = 0 ESTDP_INTF6 = ISTDP_INTF6 = 0 declare("verbosearm",0) //* templates - need to write begintemplate p2d double x[1],y[1] public x,y proc init () { if(numarg()==2) {x=$1 y=$2} } endtemplate p2d //* variables/declares declare("aid","o[1]") // finitializehandler for arm declare("nqaupd","o[1]") // NQS for arm updates (not all involving position changes) declare("nqa","o[1]") // NQS for recording arm joint angles/positions in time declare("nqE","o[1]") // has E assignment to muscle groups declare("nqDP","o[1]") // has DP assignment to muscle groups declare("MTYP","o[1]") // has muscle names declare("armSeg",2) // number of segments declare("armLen","d[2]") // length of each arm segment -- fixed throughout sim declare("MLen","d[4]") // length of each muscle -- varies throughout sim declare("MFctr",1) declare("minMLen","d[4]","maxMLen","d[4]") // min/max muscle lengths declare("rotfctr","d[2]") // multiplies diff in muscle group activation into rotation angle declare("minang","d[2]","maxang","d[2]") // min/max angles for each joint declare("aDT",5) // how often to update the whole arm apparatus declare("amoveDT",50) // how often to update the arm's position declare("mcmdspkwd",40) // motor command spike window; how wide to make the counting window (in ms) declare("EMlag",50) // lag between EM commands (at neuromuscular junction) and arm update declare("lastmovetime",0) // when was the last arm move (in ms)? declare("spdDT",50) // how often to update the muscle spindles (DP cells) declare("DPlag",25) // lag between arm movement and DP update declare("lastspdupdate",0) // when was the last spindle update (in ms)? declare("rlDT",50) // how often to check RL status declare("lastrlupdate",0) // when was the last RL update (in ms)? maxplastt_INTF6=rlDT + 50 //maxplastt_INTF6=50 declare("minRLerrchange",0.01) // minimum error change visible to RL algorithm // (units in Cartesian coordinates) declare("lrec","o[2]") // recording from ES, EM declare("vEM",new Vector()) declare("armAng","d[2]","armPos",new p2d(),"ePos",new p2d(),"tPos",new p2d()) declare("tAng","d[2]") // target angle declare("sAng","d[2]") // starting angle declare("APHASE",0,"AMOVE",0,"AHOLD",1,"ARESET",2,"HoldDur",400,"MoveDur",300,"ResetDur",100) declare("DoLearn",4,"DoReset",2,"DoAnim",1,"DoRDM",2) declare("RLMode",3) // reinforcement learning mode (0=none,1=reward,2=punishment,3=reward+punishment) declare("ATYP",new List()) declare("SUBPHASE",0) declare("ardm",new Random(),"aseed",913015) // for random set of arm pos during training declare("pnq","o[2]") declare("DiffRDEM",0) // whether to take difference from last activity when reading out EM activity declare("nqsy","o[2]") // for recording synaptic weights over time -- only saves L5 cell synapses declare("syDT",1000) // dt for recording synaptic weights into nqsy -- only used when syDT>0 // syDT set to 1000 usually, syDT = 50 when good detail needed declare("lastsysave",0) // when was the nqsy synaptic weight recording (in ms)? declare("shlock",1) // whether to lock shoulder declare("ellock",0) // whether to lock elbow declare("targid",3) // target ID for target to set up sprint(tstr,"o[%d][%d]",CTYPi+1,CTYPi+1) declare("lssyavg",tstr) // list of average synaptic weights vs time for a particular population declare("angErr",0) // whether to use diff btwn targ and arm angle for error -- defaults to 0==cartesian error declare("HoldStill",0) // whether to hold the arm still - useful for debugging EM output at a given position // declare("AdaptLearn",0) // whether to modulate learning level by distance from target //* updateMLen - update muscle lengths based on current angles proc updateMLen () { // Shoulder extensor muscle length is upper-arm length under maximum flex, // zero under minimum flex. MLen[0] = armLen[0] * (armAng[0] - minang[0]) / (maxang[0] - minang[0]) // Shoulder flexor muscle length is upper-arm length under minimum flex, // zero under maximum flex. MLen[1] = armLen[0] - MLen[0] // Elbow extensor muscle length is forearm length under maximum flex, // zero under minimum flex. MLen[2] = armLen[1] * (armAng[1] - minang[1]) / (maxang[1] - minang[1]) // Elbow flexor muscle length is forearm length under minimum flex, // zero under maximum flex. MLen[3] = armLen[1] - MLen[2] } //* resetarm - put arm in starting position proc resetarm () { local dr0,dr1 armAng[0] = sAng[0] armAng[1] = sAng[1] dr0 = deg2rad(armAng[0]) // convert to radians dr1 = deg2rad(armAng[1]) ePos.x = armLen[0] * cos(dr0) // end of elbow ePos.y = armLen[0] * sin(dr0) armPos.x = ePos.x + armLen[1] * cos(dr0 + dr1) // wrist=arm position armPos.y = ePos.y + armLen[1] * sin(dr0 + dr1) vEM.fill(0) updateMLen() } //* initarmang - init arm angles - checks lock variables proc initarmang () { if(shlock) { // shoulder-lock configuration minang[0] = -15.1 maxang[0] = -14.9 minang[1] = 0 maxang[1] = 135 sAng[0] = -15 sAng[1] = 90 } else if(ellock) { // elbow-lock straight-arm configuration minang[0] = -45 maxang[0] = 135 minang[1] = -0.1 maxang[1] = 0.1 sAng[0] = 45 sAng[1] = 0 } else { minang[0] = -45 maxang[0] = 135 minang[1] = 0 maxang[1] = 135 sAng[0] = 0 sAng[1] = 90 } // remove // sAng[0] = -15 // sAng[1] = 65 } //* assignDP - proc assignDP () { local ii,jj,shmlenrangewid,elmlenrangewid,shminmlen,shmaxmlen,elminmlen,elmaxmlen localobj xo {nqsdel(nqDP) nqDP = new NQS("col","id","ty","mid","mids","MLmin","MLmax") nqDP.strdec("mids")} // Calculate the width of each subrange. shmlenrangewid = armLen[0] / (cedp.count() / NMUSCLES) elmlenrangewid = armLen[1] / (cedp.count() / NMUSCLES) // Initialize the subrange bounds to the first subrange. shminmlen = 0 shmaxmlen = shmlenrangewid elminmlen = 0 elmaxmlen = elmlenrangewid // Loop over the different subranges... for ii=0,cedp.count() / NMUSCLES - 1 { // For the shoulder cells... for jj=0,1 { // Set the muscle length range in the particular cell. // {xo=cedp.o(ii*NMUSCLES+jj) xo.setmlenrange(shminmlen,shmaxmlen)} // Set the range out of kilter so the shoulder cells are unresponsive. {xo=cedp.o(ii*NMUSCLES+jj) xo.setmlenrange(-2,-1)} // Add the muscle length bounds to the DP NQS table. nqDP.append(1,xo.id,DP,xo.zloc,MTYP.o(xo.zloc).s,shminmlen,shmaxmlen) } // For the elbow cells... for jj=2,3 { // Set the muscle length range in the particular cell. {xo=cedp.o(ii*NMUSCLES+jj) xo.setmlenrange(elminmlen,elmaxmlen)} // Add the muscle length bounds to the DP NQS table. nqDP.append(1,xo.id,DP,xo.zloc,MTYP.o(xo.zloc).s,elminmlen,elmaxmlen) } // Move to the next subrange. shminmlen += shmlenrangewid shmaxmlen += shmlenrangewid elminmlen += elmlenrangewid elmaxmlen += elmlenrangewid } } //* initarm - init arm location/params proc initarm () { local ii armLen[0] = 1 // shoulder to forearm armLen[1] = 2 // forearm to wrist for ii=0,1 rotfctr[ii] = 1 // max of 1 degrees initarmang() // initialize arm angles, checks lock variables assignDP() // assign DP responsiveness, store info in nqDP resetarm() } //* mkmyTYP - set up names of muscle groups and phases proc mkmyTYP () { MTYP=new List() MTYP.append(new String("shext")) MTYP.append(new String("shflex")) MTYP.append(new String("elext")) MTYP.append(new String("elflex")) ATYP=new List() ATYP.append(new String("Move")) ATYP.append(new String("Hold")) ATYP.append(new String("Reset")) } //* recE - set up recording from E cells proc recE () { lrec[0] = mkrecl(col[0],ES) lrec[1] = mkrecl(col[0],EM) } //* assignEM() proc assignEM () { local i,ct,mid,a localobj vty {nqsdel(nqE) nqE = new NQS("col","id","ty","mid","mids") nqE.strdec("mids")} a=allocvecs(vty) vty.append(EM) nqE.clear(col[0].numc[EM]) for vtr(&ct,vty) for i=col[0].ix[ct],col[0].ixe[ct] { mid = i%4 nqE.append(1,i,ct,mid,MTYP.o(mid).s) } dealloc(a) } //* readoutEM - read activity from EM - store results in vEM proc readoutEM () { local i,idx,ldx,sz vEM.resize(MTYP.count) vEM.fill(0) ldx=1 for i=0,nqE.v.size-1 { idx=nqE.v[1].x(i)-col[0].ix[EM] vEM.x(nqE.v[3].x(i)) += lrec[ldx].o(1).o(idx).size lrec[ldx].o(1).o(idx).resize(0) // reset to 0 } if(DiffRDEM) { sz=nqa.v.size if(sz) { vEM.x(0) -= nqa.v[5].x(sz-1) vEM.x(1) -= nqa.v[6].x(sz-1) vEM.x(2) -= nqa.v[7].x(sz-1) vEM.x(3) -= nqa.v[8].x(sz-1) for i=0,3 if(vEM.x(i) < 0) vEM.x(i) = 0 } } } //* getarmX(shoulder angle in radians, elbow angle in radians) - // gets the end effector x coordinate func getarmX () { local ex ex = armLen[0] * cos($1) // end of elbow return ex + armLen[1] * cos($1+$2) // wrist=arm position } //* getarmY(shoulder angle in radians, elbow angle in radians) - // gets the end effector y coordinate func getarmY () { local ey ey = armLen[0] * sin($1) return ey + armLen[1] * sin($1+$2) } //* clampval(pointer to value, min value, max value) - clamps value to min,max proc clampval () { local val $&1 = MAXxy($&1,$2) $&1 = MINxy($&1,$3) } //* rotArm(angle0,angle1) - rotate arm by angle0,angle1 proc rotArm () { local dsh,delb,dr0,dr1 dsh=$1 delb=$2 armAng[0] += dsh // inc angles armAng[1] += delb for i=0,1 clampval(&armAng[i],minang[i],maxang[i]) dr0 = deg2rad(armAng[0]) // convert to radians dr1 = deg2rad(armAng[1]) ePos.x = armLen[0] * cos(dr0) // end of elbow ePos.y = armLen[0] * sin(dr0) armPos.x = ePos.x + armLen[1] * cos(dr0 + dr1) // wrist=arm position armPos.y = ePos.y + armLen[1] * sin(dr0 + dr1) updateMLen() // update the muscle lengths } //* rotArmTo(angle0,angle1) - rotate the arm to angle0,angle1 proc rotArmTo () { local dsh,delb dsh = $1 - armAng[0] delb = $2 - armAng[1] rotArm(dsh,delb) } //* getArmErr - gets current error in position func getArmErr () { if(angErr) { // error as squared difference between joint and target angles return (armAng[0] - tAng[0])^2 + (armAng[1] - tAng[1])^2 } else { // error as Cartesian distance between hand position and target position return sqrt((armPos.x - tPos.x)^2 + (armPos.y - tPos.y)^2) } } //* holdArmPos - hold arm position at target location proc holdArmPos () { local dsh,delb,ph,err if(numarg()>=2) { dsh = $1 - armAng[0] delb = $2 - armAng[1] if(numarg()>2) ph=$3 else ph=AMOVE } else { ph = AHOLD dsh = tAng[0] - armAng[0] delb = tAng[1] - armAng[1] if(APHASE == AMOVE) return // last event on queue for hold gets skipped } rotArm(dsh,delb) err = getArmErr() // gets error in position nqa.append(t,armAng[0],armAng[1],armPos.x,armPos.y,vEM.x(0),vEM.x(1),vEM.x(2),vEM.x(3),ePos.x,ePos.y,ph,MLen[0],MLen[1],MLen[2],MLen[3],SUBPHASE,err) if(DoAnim) drarm() } //* setArmPos - set/save arm position using new joint angles, based on M1 activity in vEM proc setArmPos () { local dsh,delb,ph,err if (numarg()>0) ph=$1 else ph=AMOVE dsh = rotfctr[0] * (vEM.x(1) - vEM.x(0)) // should use a sigmoid (?) delb = rotfctr[1] * (vEM.x(3) - vEM.x(2)) if(!HoldStill) rotArm(dsh,delb) err = getArmErr() // gets error in position // Add a position entry to nqa. nqa.append(t,armAng[0],armAng[1],armPos.x,armPos.y,vEM.x(0),vEM.x(1),vEM.x(2),vEM.x(3),ePos.x,ePos.y,APHASE,MLen[0],MLen[1],MLen[2],MLen[3],SUBPHASE,err) } //* updateDP - update the DP cell drive proc updateDP () { local mlentime,nqarow,nqcnum localobj xo //print "updateDP , t = " , t // The first argument is the time at which to read muscle lengths. If none // is provided, the default is the current time. if (numarg() > 0) { mlentime = $1 nqa.verbose = 0 nqarow = nqa.select("t","<=",mlentime) - 1 nqa.tog() nqa.verbose = 1 } else { mlentime = t } // Remember that we update the DP cells in nqaupd. nqcnum = nqaupd.fi("spupd") nqaupd.v[nqcnum].x(nqaupd.v.size-1) = 1 // Loop over all DP cells and update them. nqcnum = nqa.fi("ML0") for ltr(xo,cedp) { if (mlentime == t) { // Call the cell update, having it use the current MLen[zloc]. xo.updatedrive() } else { // Call the cell update passing in the appropriate muscle length. xo.updatedrive(nqa.v[nqcnum+xo.zloc].x(nqarow)) } } } //* resetArmPos - put arm back in starting position, updating nqa and vEM settings proc resetArmPos () { if(DoReset == 1) { holdArmPos(sAng[0],sAng[1],ARESET) } else if(DoReset==2) { holdArmPos(ardm.discunif(minang[0],maxang[0]),ardm.discunif(minang[1],maxang[1]),ARESET) } } //* LearnON - turn on learning proc LearnON () { plaststartT_INTF6 = 0 plastendT_INTF6 = tstop * 2 } //* LearnOFF - turn off learning proc LearnOFF () { plaststartT_INTF6 = tstop * 2 plastendT_INTF6 = tstop * 3 } //* svsywts - save synaptic weights from L5 cells proc svsywts () { local i,a localobj xo,vwg,vtau,vinc,vmaxw,vidx,vt,vpre // print "svsywts: t " , t a=allocvecs(vidx,vwg,vtau,vinc,vmaxw,vt,vpre) for ltr(xo,col[0].ce) { if(xo.type==ES) { vrsz(xo.getdvi(vidx),vwg,vtau,vinc,vmaxw,vt,vpre) xo.getplast(vwg,vtau,vinc,vmaxw) // get current weight gains vt.fill(t) //time vpre.fill(xo.id) //presynaptic id nqsy.v[0].append(vpre) nqsy.v[1].append(vidx) nqsy.v[2].append(vwg) nqsy.v[3].append(vt) } } dealloc(a) } //* RLUpdate - reinforcement learning updateupdate proc RLUpdate () { local err1,err2,nqcnum // If we have at least 2 nqa entries... if (nqa.v.size > 1) { // Find the distance error of the 2nd-to-last hand position (in // nqa) from the target hand position. err1 = nqa.getcol("err").x(nqa.v.size-2) // Find the distance error of the last hand position (in // nqa) from the target hand position. err2 = nqa.getcol("err").x(nqa.v.size-1) if(verbosearm) print "err1 : ", err1, " err2 " , err2 if(DoLearn==2) { if(err2< err1) LearnON() else LearnOFF() } else if(DoLearn==3) { // RL with STDP erasure if go wrong way if(err2 >= err1) { setplastnq(pnq[0],col[0]) } nqsdel(pnq[0]) pnq[0]=getplastnq(col[0]) } else if ((DoLearn == 4) && (RLMode > 0)) { // RL with dopamine nqcnum = nqaupd.fi("reinf") if ((err1 - err2 >= minRLerrchange) && ((RLMode == 1) || (RLMode == 3))) { // if(AdaptLearn) EPOTW_INTF6 = (err1 - err2) / err1 // else EPOTW_INTF6=1 col[0].ce.o(0).dopelearn(1) // LTP nqaupd.v[nqcnum].x(nqaupd.v.size - 1) = 1 // put in nqaupd } else if ((err2 - err1 >= minRLerrchange) && ((RLMode == 2) || (RLMode == 3))) { // if(AdaptLearn) EDEPW_INTF6 = (err2 - err1) / err1 // else EDEPW_INTF6=1 col[0].ce.o(0).dopelearn(-1) // LTD nqaupd.v[nqcnum].x(nqaupd.v.size - 1) = -1 // put in nqaupd } } } } //* resetPhase - check which movement phase (move or hold) and set plasticity params proc resetPhase () { local md SUBPHASE = 0 if(APHASE==AMOVE) { // phase was move, now set to hold/learn APHASE = AHOLD cvode.event(t+HoldDur,"resetPhase()") LearnON() } else if(APHASE==AHOLD) { // phase was hold/learn LearnOFF() // turn off learning if(DoReset) { APHASE = ARESET // now set to reset cvode.event(t+ResetDur,"resetPhase()") } else { APHASE = AMOVE // now set to move if(DoRDM==2) { md = MAXxy(aDT,aDT*int(ardm.negexp(MoveDur/aDT))) cvode.event(t+md,"resetPhase()") } else { cvode.event(t+MoveDur,"resetPhase()") } } } else if(APHASE==ARESET) { // phase waws reset, now set to move APHASE = AMOVE if(DoRDM==2) { md = MAXxy(aDT,aDT*int(ardm.negexp(MoveDur/aDT))) cvode.event(t+md,"resetPhase()") } else { cvode.event(t+MoveDur,"resetPhase()") } } print "t : ", t , " set phase to ", ATYP.o(APHASE).s } //* updateArm - update all arm apparatus // This includes muscle commands, joints, muscle spindles, // and reinforcement learning signals. proc updateArm () { local err,nqcnum // print "updateArm , t = " , t // Read the M1 muscle group command spike counts since updateArm() call. // These go into vEM. readoutEM() // Zero out the shoulder motor commands. vEM.x(0) = 0 vEM.x(1) = 0 // If we are at the beginning of the simulation... if (t == aDT) { // Set up an arm update entry for t=0. nqaupd.append(0,0,0,0,0,0,0,0) // Set up an arm position entry for t=0. err = getArmErr() nqa.append(0,armAng[0],armAng[1],armPos.x,armPos.y,vEM.x(0),vEM.x(1),vEM.x(2),vEM.x(3),ePos.x,ePos.y,APHASE,MLen[0],MLen[1],MLen[2],MLen[3],SUBPHASE,err) updateDP() // Set up the initial DP cell activity. // cvode.event(t+100000,"updateTarget()") // Post the next updateTarget() event. } // Add vEM to the muscle commands part of the arm update table, nqaupd. nqaupd.append(t,vEM.x(0),vEM.x(1),vEM.x(2),vEM.x(3),0,-1,0) // If it's time for another arm motion... if ((t - lastmovetime >= amoveDT) && (t >= mcmdspkwd + EMlag)) { // Get all of the motor command entries in a window mcmdspkwd ms wide, back // EMlag ms from the current time t. // NOTE: The window here is the right width, but the boundaries are not quite // what you'd expect for spike times. Example, if the nqa entry is at t=100 ms, // mcmdspkwd = 40 ms, and EMlag = 50 ms, and the nqaupd entries are once every // 5 ms, then you might think that tallied range from t=10 ms to t=50 ms. In // fact, nqaupd entries: 10,15,20,25,30,35,40,45 are chosen which means that // the actual spike times are from t=5 to 45 ms. // GLC 4/17/12: The "[)" should maybe be changed to "(]" post-paper. nqaupd.verbose = 0 nqaupd.select("t","[)",t-mcmdspkwd-EMlag,t-EMlag) // Set vEM to the sum of each of the relevant motor command entries. vEM.x(0) = nqaupd.getcol("shext").sum() vEM.x(1) = nqaupd.getcol("shflex").sum() vEM.x(2) = nqaupd.getcol("elext").sum() vEM.x(3) = nqaupd.getcol("elflex").sum() // Set the database back. nqaupd.tog() nqaupd.verbose = 1 setArmPos() // Set the arm position. if (DoAnim) drarm() // Draw the arm configuration. lastmovetime = t // Remember the time of the event. // Add the nqa row number to the nqaupd table. nqcnum = nqaupd.fi("mvmt") nqaupd.v[nqcnum].x(nqaupd.v.size - 1) = nqa.v.size - 1 } // If it's time for a muscle spindle cell update... if ((t - lastspdupdate >= spdDT) && (t >= DPlag)) { updateDP(t-DPlag) // Update DP cell activity. lastspdupdate = t // Remember the time of the event. } // If it's time for an RL update... if ((DoLearn==2 || DoLearn==3 || DoLearn==4) && (t - lastrlupdate > rlDT)) { RLUpdate() // Do an RL update. lastrlupdate = t // Remember the time of the event. } if (syDT > 0 && t - lastsysave >= syDT) { // time for nqsy synaptic save // Remove this conditional line later... /* if (t <= 10000) { svsywts() // Do a synaptic save. } */ svsywts() // Do a synaptic save. lastsysave = t // Remember the time of the event. } cvode.event(t+aDT,"updateArm()") // Post the next updateArm() event. } //* updateTarget - set a new target proc updateTarget () { /* if (targid == 3) { targid = 1 } else if (targid == 1) { targid = 4 } else if (targid == 4) { targid = 0 } else if (targid == 0) { targid = 2 } else if (targid == 2) { targid = 3 } */ if (targid == 1) { targid = 2 } else if (targid == 2) { targid = 3 } else if (targid == 3) { targid = 4 } else if (targid == 4) { targid = 0 } else if (targid == 0) { targid = 1 } if (targid == 0) { setTarg(-15,135) // extreme flexion target } else if (targid == 1) { setTarg(-15,105) } else if (targid == 2) { setTarg(-15,75) // normal target } else if (targid == 3) { setTarg(-15,35) } else if (targid == 4) { setTarg(-15,0) // extreme extension target } cvode.event(t+100000,"updateTarget()") // Post the next updateTarget() event. } //* initArmCB - initialize arm callbacks proc initArmCB () { local i // Set up the muscle commands NQS table. nqsdel(nqaupd) nqaupd = new NQS("t","shext","shflex","elext","elflex","reinf","mvmt","spupd") // Set up the arm NQS table. {nqsdel(nqa) nqa=new NQS("t","ang0","ang1","x","y","shext","shflex","elext","elflex","ex","ey","phase")} nqa.resize("ML0","ML1","ML2","ML3","subphase","err") // Clear out the current muscle command array vEM. if(vEM==nil) vEM = new Vector(MTYP.count) else {vEM.resize(MTYP.count) vEM.fill(0)} resetarm() // reset cvode.event(aDT,"updateArm()") if(DoLearn==3 || DoLearn==4) { LearnON() for i=0,numcols-1 pnq[i]=getplastnq(col[i]) } else LearnOFF() APHASE=AMOVE if(DoRDM) ardm.ACG(aseed) if(DoLearn==1) cvode.event(MoveDur,"resetPhase()") SUBPHASE=0 if(syDT>0) {nqsdel(nqsy) nqsy=new NQS("id1","id2","wg","t") cvode.event(syDT,"svsywts()")} lastmovetime = lastspdupdate = lastrlupdate = lastsysave = 0 } //* mkaid - setup handlers for EM readout -> arm motion proc mkaid () { aid = new FInitializeHandler(1,"initArmCB()") } //* drarm([nqa,row from nqa,erase]) - draw arm location proc drarm () { local idx,ex,ey,wx,wy,ang0,ang1,ers,ln,xsz localobj nqa,s ers=1 xsz=0.15 ln=armLen[0]+armLen[1] g.size(-ln,ln,-ln,ln) s=new String() if(numarg()>=2) { {nqa=$o1 idx=$2} if(idx < 0 || idx >= nqa.v.size) {printf("drarm ERRA: invalid index: %d,%d\n",idx,nqa.v.size) return} ex = nqa.v[9].x(idx) ey = nqa.v[10].x(idx) wx = nqa.v[3].x(idx) wy = nqa.v[4].x(idx) ang0 = nqa.v[1].x(idx) ang1 = nqa.v[2].x(idx) if(numarg()>=3)ers=$3 if(DoLearn) { sprint(s.s,"t=%g: %g %g %s",nqa.v[0].x(idx),ang0,ang1,ATYP.o(nqa.v[11].x(idx)).s) } else { sprint(s.s,"t=%g: %g %g",nqa.v[0].x(idx),ang0,ang1) } } else { if(numarg()>=1)ers=$1 ex = ePos.x ey = ePos.y wx = armPos.x wy = armPos.y ang0 = armAng[0] ang1 = armAng[1] if(DoLearn) { sprint(s.s,"t=%g: %g %g %s",t,ang0,ang1,ATYP.o(APHASE).s) } else { sprint(s.s,"t=%g: %g %g",t,ang0,ang1) } } if(verbose) print "ex=",ex,"ey=",ey,"ang0=",ang0,"wx=",wx,"wy=",wy,"ang1=",ang1 if(ers) g.erase_all() drline(tPos.x-xsz,tPos.y-xsz,tPos.x+xsz,tPos.y+xsz,g,1,4) // draw an x for the target drline(tPos.x-xsz,tPos.y+xsz,tPos.x+xsz,tPos.y-xsz,g,1,4) drline(0,0,ex,ey,g,2,4) // draw arm drline(ex,ey,wx,wy,g,3,4) g.label(0.55,0.95,s.s) g.flush() doNotify() } //* nqa2gif(nqa[,inc]) - saves arm locations in nqa as gif // inc specifies how many frames to skip proc nqa2gif () { local i,j,inc localobj nqa,s nqa=$o1 s=new String() j=0 if(numarg()>1) inc=$2 else inc=1 for(i=0;i1) is=$2 else is=0 if(numarg()>2) ie=$3 else ie=nqa.v.size-1 if(numarg()>3) del=$4 else del=0.25e9 for i=is,ie { drarm(nqa,i) sleepfor(0,del) } } //* setTarg(angle0,angle1) - set target angle proc setTarg () { local dr0,dr1,ex,ey tAng[0] = $1 tAng[1] = $2 dr0 = deg2rad(tAng[0]) // convert to radians dr1 = deg2rad(tAng[1]) ex = armLen[0] * cos(dr0) // end of elbow ey = armLen[0] * sin(dr0) tPos.x = ex + armLen[1] * cos(dr0 + dr1) // target position for end of arm in x,y tPos.y = ey + armLen[1] * sin(dr0 + dr1) } //* IterTrain(number of iterations, number of increments, duration for each starting position) proc IterTrain () { local nangs,i,j,k,dur,niter,a localobj vinc a=allocvecs(vinc) vinc.resize(2) niter=$1 nangs=$2 dur=$3 resetplast_INTF6=0 for i=0,1 vinc.x(i) = (maxang[i]-minang[i])/nangs for i=0,niter-1 { print "iteration number " , i for j=0,1 sAng[j] = minang[j] for j=0,nangs { tstop = dur run() for k=0,1 { // increment starting position sAng[k] += vinc.x(k) if(sAng[k] > maxang[k]) sAng[k]=maxang[k] } } } dealloc(a) } //* IterTrain2D(number of iterations, number of increments, duration for each starting position) proc IterTrain2D () { local nangs,i,j,k,l,dur,niter,a localobj vinc a=allocvecs(vinc) vinc.resize(2) niter=$1 nangs=$2 dur=$3 resetplast_INTF6=0 for i=0,1 vinc.x(i) = (maxang[i]-minang[i])/nangs for i=0,niter-1 { sAng[0]=minang[0] l=0 // subiteration counter for j=0,nangs { sAng[1]=minang[1] for k=0,nangs { print "train: iter ", i, ", subiter ", l tstop = dur run() sAng[1] += vinc.x(1) // inc elbow angle if(sAng[1] > maxang[1]) sAng[1]=maxang[1] l += 1 // subiteration } sAng[0] += vinc.x(0) // inc shoulder angle if(sAng[0] > maxang[0]) sAng[0]=maxang[0] } } dealloc(a) } //* IterTest(number of iterations, number of increments, duration for each starting position[,control]) // NB: WHEN control==1, the weights will be reset to baseline, SO USE WITH CARE!!!!!!!!!!!!! obfunc IterTest () { local ii,nangs,i,j,k,dur,niter,dltmp,ctl,a localobj vinc,nqo a=allocvecs(vinc) vinc.resize(2) niter=$1 nangs=$2 dur=$3 if(numarg()>3) ctl=$4 else ctl=0 if(ctl) { print "IterTest WARNING: reset weights to baseline!" resetplast_INTF6=1 } else resetplast_INTF6=0 dltmp=DoLearn DoLearn=0 // turn off learning for i=0,1 vinc.x(i) = (maxang[i]-minang[i])/nangs vinc.x(0) = 0.0 // no shoulder increment for i=0,niter-1 { for j=0,1 sAng[j] = minang[j] sAng[0] = -15.0 // hold shoulder angle at -15 degrees for j=0,nangs { print "test: iter ", i, ", subiter ", j tstop = dur run() {nqa.resize("iter") nqa.pad() nqa.v[nqa.m-1].fill(i)} {nqa.resize("subiter") nqa.pad() nqa.v[nqa.m-1].fill(j)} {nqa.resize("sAng0") nqa.pad() nqa.v[nqa.m-1].fill(sAng[0])} {nqa.resize("sAng1") nqa.pad() nqa.v[nqa.m-1].fill(sAng[1])} AddCountCol(nqa) // add spike counts if(nqo==nil) {nqo=new NQS() nqo.cp(nqa)} else {nqo.append(nqa)} for k=0,1 { // increment starting position sAng[k] += vinc.x(k) if(sAng[k] > maxang[k]) sAng[k]=maxang[k] } } } {DoLearn=dltmp dealloc(a) return nqo} } //* AddCountCol(nqa) - add a column of spike counts to nqa. assumes arm in same position in the nqa. proc AddCountCol () { local i,j,tcind,mint,maxt,nc,a localobj nqa,vcnt,vrt {a=allocvecs(vcnt,vrt) nqa=$o1} nc = col[0].cellsnq.size() // get the number of cells from cellsnq mksnq() // make the NQS with spike info {nqa.tog("DB") nqa.resize("vcnt","vrt") nqa.odec("vcnt") nqa.odec("vrt") nqa.pad()} snq.verbose=0 vcnt.resize(nc) vrt.resize(nc) // Remember the column index for "t" in nqa. tcind = nqa.fi("t") // Loop over all the nqa entries... for i = 0, nqa.v.size-1 { // If we are in the first entry (t=0)... if (i == 0) { // Set spike counts and rates to all zeros. vcnt.resize(0) vcnt.resize(nc) vrt.resize(0) vrt.resize(nc) } else { // The minimum time is the previous nqa entry time, and the max. is the current // entry. mint = nqa.v[tcind].x(i-1) maxt = nqa.v[tcind].x(i) // Loop over all cell indices... for j = 0, nc - 1 { // Count only those spikes from the right time interval and cell id. vcnt.x(j) = snq.select("id",j,"t","(]",mint,maxt) // Calculate the rate in spikes / s. vrt.x(j) = vcnt.x(j) * 1e3 / (maxt-mint) } } // Set the vcnt and vrt entries for this nqa entry. nqa.set("vcnt",i,vcnt) nqa.set("vrt",i,vrt) } snq.verbose=1 dealloc(a) } //* AddCountCol(nqa) - add a column of spike counts to nqa. assumes arm in same position in the nqa. proc AddCountColOld () { local i,mint,maxt,nc,a localobj nqa,vcnt {a=allocvecs(vcnt) nqa=$o1} nc = col[0].cellsnq.size() // get the number of cells from cellsnq mksnq() // make the NQS with spike info {nqa.tog("DB") nqa.resize("vcnt") nqa.odec("vcnt") nqa.pad()} snq.verbose=0 vcnt.resize(nc) mint=nqa.getcol("t").min maxt=nqa.getcol("t").max // Instead of having 1 vcnt which is padded everywhere, vcnt spike counts // should be specific to the time-interval since last nqa entry. for i = 0, nc - 1 vcnt.x(i) = 1e3 * snq.select("id",i) / (maxt-mint) // count rate over full snq,nqa (units in spikes / s) for i = 0, nqa.v.size-1 nqa.set("vcnt",i,vcnt) snq.verbose=1 dealloc(a) } //* IterTest2D(number of iterations, number of increments, duration for each starting position[,control,savecounts]) // NB: WHEN control==1, the weights will be reset to baseline, SO USE WITH CARE!!!!!!!!!!!!! // savecounts flag specifies whether to add a column to the output NQS that has # of spikes of each // cell at each position (a Vector column) obfunc IterTest2D () { local ii,nangs,i,j,k,l,dur,niter,dltmp,ctl,savec,a localobj vinc,nqo a=allocvecs(vinc) vinc.resize(2) niter=$1 nangs=$2 dur=$3 if(numarg()>3) ctl=$4 else ctl=0 if(numarg()>4) savec=$5 else savec=0 if(ctl) { print "IterTest2D WARNING: reset weights to baseline!" resetplast_INTF6=1 } else resetplast_INTF6=0 dltmp=DoLearn DoLearn=0 // turn off learning for i=0,1 vinc.x(i) = (maxang[i]-minang[i])/nangs for i=0,niter-1 { sAng[0]=minang[0] l=0 // subiteration counter for j=0,nangs { sAng[1]=minang[1] for k=0,nangs { print "test: iter ", i, ", subiter ", l tstop = dur run() {nqa.resize("iter") nqa.pad() nqa.v[nqa.m-1].fill(i)} {nqa.resize("subiter") nqa.pad() nqa.v[nqa.m-1].fill(l)} {nqa.resize("sAng0") nqa.pad() nqa.v[nqa.m-1].fill(sAng[0])} {nqa.resize("sAng1") nqa.pad() nqa.v[nqa.m-1].fill(sAng[1])} if(savec) AddCountCol(nqa) if(nqo==nil) {nqo=new NQS() nqo.cp(nqa)} else {nqo.append(nqa)} sAng[1] += vinc.x(1) // inc elbow angle if(sAng[1] > maxang[1]) sAng[1]=maxang[1] l += 1 // subiteration } sAng[0] += vinc.x(0) // inc shoulder angle if(sAng[0] > maxang[0]) sAng[0]=maxang[0] } } {DoLearn=dltmp dealloc(a) return nqo} } //* addhyperrcols(nqs from IterTest or IterTest2D) - adds hypothetical moves/errors from // a IterTest with HoldStill==1, to see what the output moves would be // with fixed positions and then to calculate the errors proc addhyperrcols () { local i,s0,s1,errsh,errel,errp,sx,sy,nx,ny,err0,err1,dx,dy localobj nqo nqo=$o1 nqo.tog("DB") if(nqo.fi("dy")==-1) { nqo.resize("dirsh","direl","errsh","errel","errp","errxy","dx","dy") nqo.pad() } for i=0,nqo.v.size-1 { dirsh = nqo.getcol("shflex").x(i) - nqo.getcol("shext").x(i) // shoulder rot command direl = nqo.getcol("elflex").x(i) - nqo.getcol("elext").x(i) // elbow rot command s0 = nqo.getcol("sAng0").x(i) // starting angle s1 = nqo.getcol("sAng1").x(i) if(abs(s0 + dirsh - tAng[0] ) < abs(s0 - tAng[0])) { errsh = -abs(dirsh) // angular error was reduced } else errsh = abs(dirsh) // angular error was increased if(abs(s1 + direl - tAng[1] ) < abs(s1 - tAng[1])) { errel = -abs(direl) // angular error was reduced } else errel = abs(direl) // angular error was increased errp = errsh + errel sx=getarmX(deg2rad(s0),deg2rad(s1)) // starting x sy=getarmY(deg2rad(s0),deg2rad(s1)) // starting y err0 = sqrt( (sx-tPos.x)^2 + (sy-tPos.y)^2) // starting error nx=getarmX(deg2rad(s0+dirsh),deg2rad(s1+direl)) // new x ny=getarmY(deg2rad(s0+dirsh),deg2rad(s1+direl)) // new y err1 = sqrt( (nx-tPos.x)^2 + (ny-tPos.y)^2) // error after hypothetical move if(err10)ct=$1 else ct=DP if(numarg()>1)cdx=$2 else cdx=0 if(ct==DP) nqrf.resize("mid","ml") if(snq[cdx]==nil) {i=CDX CDX=cdx mksnq() CDX=i} nqrf.clear(snq[cdx].select("type",DP)) snq[cdx].tog("DB") nqa.tog("DB") nqa.verbose=snq[cdx].verbose=0 tdx=0 tt=snq[cdx].getcol("t").x(0) for i=1,nqa.v.size-1 { if(i%10==0)printf(".") while(tt < nqa.getcol("t").x(i) && tdx < snq[cdx].v.size) { if(snq[cdx].getcol("type").x(tdx)==ct) { id=snq[cdx].getcol("id").x(tdx) ang0=nqa.getcol("ang0").x(i-1) ang1=nqa.getcol("ang1").x(i-1) // cedp.o(id).subtype if(ct==DP) { mid=cedp.o(id-col[cdx].ix[ct]).zloc ml=nqa.v[mid+12].x(i-1) nqrf.append(i-1,id,ct,ang0,ang1,nqa.v[12].x(i-1),nqa.v[13].x(i-1),nqa.v[14].x(i-1),nqa.v[15].x(i-1),tt,mid,ml) } else { nqrf.append(i-1,id,ct,ang0,ang1,nqa.v[12].x(i-1),nqa.v[13].x(i-1),nqa.v[14].x(i-1),nqa.v[15].x(i-1),tt) } } tdx += 1 if(tdx2)nrm=$3 else nrm=1 a=allocvecs(vx,vy,vcnt) vrsz(nq.getcol("x").uniq,vx,vy) nq.getcol("x").uniq(vx) nq.getcol("y").uniq(vy) nqrf=new NQS("x","y","cnt") for i=0,nq.v.size-1 { x = nq.getcol("x").x(i) y = nq.getcol("y").x(i) cnt=nq.get("vcnt",i).o.x(id) if(nqrf.select(-1,"x",x,"y",y)) { // The line below doesn't make sense if vcnt counts span all times in snq. nqrf.v[2].x(nqrf.ind.x(0)) += cnt } else nqrf.append(x,y,cnt) } dealloc(a) if(nrm) nqrf.getcol("cnt").div(nqrf.getcol("cnt").max()) return nqrf } //* drcellrf(nqs from getcellrf[,sz]) - draws the count per position using data from the nqs // green dots are starting x,y . blue is ending. red line height is # spikes @ x,y. // sz is the height,width of markers for the x,y positions // the nqs ($o1) is obtained from getcellrf proc drcellrf () { local i,x,y,n,sz localobj nqr nqr=$o1 if(numarg()>1)sz=$2 else sz=0.01 for i=0,nqr.v.size-1 { x = nqr.v[0].x(i) y = nqr.v[1].x(i) n = nqr.v[2].x(i) drline(x,y,x,y+n,g,2,3) drline(x-sz,y-sz,x+sz,y-sz,g,4,3) drline(x-sz,y+n+sz,x+sz,y+n+sz,g,3,3) } } // // Analysis functions (adding these here to avoid putting them always in notebook) // //* drelbowtrajectory -- show the elbows trajectory vs. the target angle proc drelbowtrajectory () { if(numarg()>0) g.erase_all() nqa.gr("ang1","t",0,3,0) drline(0,tAng[1],t,tAng[1],g,3,2) drline(0,minang[1],t,minang[1],g,1,3) drline(0,maxang[1],t,maxang[1],g,1,3) } //* drshouldertrajectory -- show the shoulder's trajectory vs. the target angle proc drshouldertrajectory () { if(numarg()>0) g.erase_all() nqa.gr("ang0","t",0,2,0) drline(0,tAng[0],t,tAng[0],g,2,2) drline(0,minang[0],t,minang[0],g,9,3) drline(0,maxang[0],t,maxang[0],g,9,3) } //* addnqacol2nqaupd proc addnqacol2nqaupd () { local nqcnum,nqcnum2,nqcnum3,nqaind,ii nqaupd.resize($s1) // add column to look at nqcnum = nqaupd.fi($s1) nqcnum2 = nqaupd.fi("mvmt") nqcnum3 = nqa.fi($s1) nqaupd.v[nqcnum].resize(nqaupd.v.size) // pad the column with zeros nqaind = -1 for ii=0,nqaupd.v.size-1 { if ((nqaupd.v[nqcnum2].x(ii) != -1) && (nqaupd.v[nqcnum2].x(ii) != nqaind)) { nqaind = nqaupd.v[nqcnum2].x(ii) } nqaupd.v[nqcnum].x(ii) = nqa.v[nqcnum3].x(nqaind) } } //* getavgsyvst(nqsy,ty1,ty2[,CDX]) - get average synaptic weight vs time obfunc getavgsyvst () { local i,ty1,ty2,cdx localobj ls,vt,vwg,nqsy,vids // Read the parameters in. nqsy=$o1 ty1=$2 ty2=$3 if(numarg()>3) cdx=$4 else cdx=0 // Create a new list containing a Vector of times vt and average weights vwg. ls=new List() ls.append(vt=new Vector(nqsy.v.size)) ls.append(vwg=new Vector(nqsy.v.size)) vwg.resize(0) nqsy.tog("DB") nqsy.getcol("t").uniq(vt) nqsy.verbose=0 // If we are not mapping to EM, do the normal method if getting the means. if (ty2 != EM) { for vtr(&i,vt) if(nqsy.select("t",i,"id1","[]",col[cdx].ix[ty1],col[cdx].ixe[ty1],"id2","[]",col[cdx].ix[ty2],col[cdx].ixe[ty2])) { vwg.append(nqsy.getcol("wg").mean) } // Otherwise, do the method that eliminates the shoulder EM cells. } else { // Set up a vector that just has the cell IDs that are elbow-related. vids = new Vector(0) for i=col[cdx].ix[ty2],col[cdx].ixe[ty2] { if ((i - col[cdx].ix[ty2]) % 4 > 1) vids.append(i) } for vtr(&i,vt) if(nqsy.select("t",i,"id1","[]",col[cdx].ix[ty1],col[cdx].ixe[ty1],"id2","EQW",vids)) { vwg.append(nqsy.getcol("wg").mean) } } nqsy.verbose=1 return ls // return list with vector of times and average weights } //* mkavgsyvst(nqsy) proc mkavgsyvst () { local i,j localobj str,nqsy str=new String() nqsy=$o1 for case(&i,ES) for case(&j,EM) if(pmat[0][0][i][j]) { print CTYP.o(i).s,"->",CTYP.o(j).s lssyavg[i][j] = getavgsyvst(nqsy,i,j) sprint(str.s,"%s->%s",CTYP.o(i).s,CTYP.o(j).s) lssyavg[i][j].o(1).label(str.s) } } //* drxyvecfield(nqo) - draw xy trajectory from IterTest or IterTest2D NQS // that had addhyperrefcol called on it - this NQS should be generated // post learning with HoldStill set to 1 proc drxyvecfield () { local eidx,i,x0,y0,x1,y1,xsz,a localobj vx1,vy1,vx2,vy2,nqo nqo=$o1 {a=allocvecs(vx1,vx2,vy1,vy2) vrsz(0,vx1,vx2,vy1,vy2)} {nqo.verbose=0 nqo.tog("DB") eidx=nqo.getcol("subiter").max()} for i=0,eidx if(nqo.select("subiter",i)) { x0 = nqo.getcol("x").mean() y0 = nqo.getcol("y").mean() x1 = x0 + nqo.getcol("dx").mean() y1 = y0 + nqo.getcol("dy").mean() drline(x0,y0,x1,y1,g,2,3) {vx1.append(x0) vy1.append(y0) vx2.append(x1) vy2.append(y1)} } nqo.verbose=1 vy1.mark(g,vx1,"O",6,4,1) // start (green) vy2.mark(g,vx2,"O",6,3,1) // end (blue) xsz=0.1 drline(tPos.x-xsz,tPos.y-xsz,tPos.x+xsz,tPos.y+xsz,g,1,4) // draw an x for the target drline(tPos.x-xsz,tPos.y+xsz,tPos.x+xsz,tPos.y-xsz,g,1,4) dealloc(a) } //* drrotvecfield(nqo[,xsz]) - draw angular trajectory from IterTest or IterTest2D NQS // that had addhyperrefcol called on it - this NQS should be generated // post learning with HoldStill set to 1. xsz is size of target proc drrotvecfield () { local eidx,i,s0,s1,n0,n1,xsz,a localobj vx1,vy1,vx2,vy2,nqo {nqo=$o1 a=allocvecs(vx1,vx2,vy1,vy2) vrsz(0,vx1,vx2,vy1,vy2)} {nqo.verbose=0 nqo.tog("DB") eidx=nqo.getcol("subiter").max()} for i=0,eidx if(nqo.select("subiter",i)) { s0 = nqo.getcol("sAng0").mean() s1 = nqo.getcol("sAng1").mean() n0 = s0 + nqo.getcol("dirsh").mean() n1 = s1 + nqo.getcol("direl").mean() drline(s0,s1,n0,n1,g,2,3) {vx1.append(s0) vy1.append(s1) vx2.append(n0) vy2.append(n1)} } nqo.verbose=1 vy1.mark(g,vx1,"O",6,4,1) // start (green) vy2.mark(g,vx2,"O",6,3,1) // end (blue) if(numarg()>1) xsz=$2 else xsz=5 drline(tAng[0]-xsz,tAng[1]-xsz,tAng[0]+xsz,tAng[1]+xsz,g,1,4) // draw an x for the target drline(tAng[0]-xsz,tAng[1]+xsz,tAng[0]+xsz,tAng[1]-xsz,g,1,4) dealloc(a) } //* run and init nqs objects proc myrun () { local i localobj xo run() // Do the normal run. // For all of the columns, make spike NQS tables, snq[]. for CDX=0,numcols-1 { {nqsdel(snq[CDX]) snq[CDX]=SpikeNQS(vit[CDX].tvec,vit[CDX].vec,col[CDX])} snq[CDX].marksym="O" print "CDX:",CDX pravgrates() // Show average firing rates for each column. } CDX=0 // Load the analysis tables for column 0. initMyNQs() // make all of the usual analysis tables. // initAllMyNQs() // make all of the desired synaptic weights. if(syDT) mkavgsyvst(nqsy) } //* mysv - save output after myrun proc mysv () { localobj s,nq s = new String() CDX=0 // Save the snq for column 0. sprint(s.s,"data/%s_%s_snq.nqs",$s1,col[CDX].name) {snq[CDX].tog("DB") snq[CDX].sv(s.s)} // Save the global LFP for column 0. /* sprint(s.s,"data/%s_%s_LFP.nqs",$s1,col[CDX].name) {nqLFP[CDX].tog("DB") nqLFP[CDX].sv(s.s)} */ // Save the nqa table. {sprint(s.s,"data/%s_nqa.nqs",$s1) nqa.tog("DB") nqa.sv(s.s)} // Save an nq for the average synaptic weight changes. if(syDT) { nq = new NQS("t","EStoEMavgwtgn") nq.setcol("t",lssyavg[ES][EM].o(0)) nq.setcol("EStoEMavgwtgn",lssyavg[ES][EM].o(1)) sprint(s.s,"data/%s_EStoEMavgwtgn.nqs",$s1) {nq.tog("DB") nq.sv(s.s)} nqsdel(nq) } /* {sprint(s.s,"/u/samn/intfstdp/data/%s_snq.nqs",$s1) snq.tog("DB") snq.sv(s.s)} if(nqrat!=nil) { {sprint(s.s,"/u/samn/intfstdp/data/%s_nqrat.nqs",$s1) nqrat.tog("DB") nqrat.sv(s.s)} } if(mynqp.size>0) { {sprint(s.s,"/u/samn/intfstdp/data/%s_mynqp.nqs",$s1) mynqp.tog("DB") mynqp.sv(s.s)} } {nqsdel(wgnq) wgnq=mkwgnq(col) sprint(s.s,"/u/samn/intfstdp/data/%s_wgnq.nqs",strv) wgnq.sv(s.s)} */ } //* myrunsv(simstr) - run & save output proc myrunsv () { myrun() mysv($s1) } //* mytrainrunsv(simstr[,iters,numlevels,dur,twod]) - run with training and save weights proc mytrainrunsv () { local iters,nl,dur,twod localobj str str=new String2() if(numarg()>1) iters=$2 else iters=100 if(numarg()>2) nl=$3 else nl=15 if(numarg()>3) dur=$4 else dur=10e3 if(numarg()>4) twod=$5 else twod=0 sprint(str.s,"/u/samn/arm2d/data/%s_scale%d_iters%d_nlevels%d_dur%g_twod%d",$s1,scale,iters,nl,dur,twod) if(twod) IterTrain2D(iters,nl,dur) else IterTrain(iters,nl,dur) {nqsdel(nq[0]) nq[0]=mkwgnq(col[0]) sprint(str.t,"%s_wgnq_A1.nqs",str.s) nq[0].sv(str.t)} print "saved ", str.t {nqsdel(nq[1]) nq[1]=getplastnq(col[0]) sprint(str.t,"%s_plastnq_A2.nqs",str.s) nq[1].sv(str.t)} print "saved ", str.t } //* func calls mkmyTYP() initarm() assignEM() recE() mkaid() if (targid == 0) { setTarg(-15,135) // extreme flexion target } else if (targid == 1) { setTarg(-15,105) } else if (targid == 2) { setTarg(-15,75) // normal target } else if (targid == 3) { setTarg(-15,35) } else if (targid == 4) { setTarg(-15,0) // extreme extension target } //setTarg(45,0) // straight arm locked-elbow target if(DoAnim) gg() LearnOFF() // start with no learning