2 PYLAB_INTERACTIVE_VERBOSE = True
5 LOG_DIR = '$DEFAULT$/unfold'
15 from numpy import arange, sin, pi, isnan
18 if PYLAB_INTERACTIVE_VERBOSE :
19 from pylab import figure, plot, title, legend, hold, subplot, draw
20 import time # for timestamping lines on plots
22 EFILE = file(os.path.expanduser('~/rsrch/debug.unfold'), 'a')
24 class ExceptionTooClose (Exception) :
26 The piezo is too close to the surface,
27 an unfolding would extend past pzMin
29 class ExceptionTooFar (Exception) :
31 The piezo has reached pzMax without
32 reaching the desired deflection setpoint.
35 def plot_dict(d, label) :
37 plot(d["Z piezo output"], d["Deflection input"], '.',label=label)
38 except KeyError, string:
40 raise KeyError, string
42 class unfold_data_log (data_logger.data_log) :
43 def save(self, unfold, timestamp, CTemp, VSetpoint, nmBindPos,
45 nmDist, nmStep, ppStep, nmPsRate, freq,
46 approach_data, unfold_data) :
47 filepath,timestamp = self.get_filename(timestamp)
48 # save the unfolding data
49 dataFile = open(filepath, "w")
50 for i in range(0, len(unfold_data["Z piezo output"])) :
51 dataFile.write("%d\t%d\t%d\n" % (unfold_data["Z piezo output"][i],
52 unfold_data["Deflection input"][i],
53 unfold_data["Z piezo input"][i]))
55 dataFile = open(filepath+"_approach", "w")
56 for i in range(0, len(approach_data["Z piezo output"])) :
57 dataFile.write("%d\t%d\n" % (approach_data["Z piezo output"][i],
58 approach_data["Deflection input"][i]))
62 paramFile = open(filepath+"_param", "w")
63 paramFile.write("Environment\n")
64 paramFile.write("Time:\t"+timestamp+"\n")
66 paramFile.write("Temperature (C):\t"+str(CTemp)+"\n")
68 paramFile.write("\nPiezo parameters\n")
69 paramFile.write("Z piezo sensitivity (nm/Vp):\t"+str(unfold.zp.sensitivity)+"\n")
70 paramFile.write("Z piezo gain (Vp/Vo):\t"+str(unfold.zp.gain)+"\n")
71 paramFile.write("X piezo sensitivity (nm/Vp):\t"+str(unfold.xp.xpSensitivity)+"\n")
72 paramFile.write("X piezo gain (Vp/Vo):\t"+str(unfold.xp.gain)+"\n")
73 paramFile.write("X piezo position (nm):\t"+str(unfold.xp.out2nm(unfold.xp.curPos()))+"\n")
75 paramFile.write("\nApproach parameters\n")
76 paramFile.write("Setpoint (V):\t"+str(VSetpoint)+"\n")
77 paramFile.write("Bind pos (nm):\t"+str(nmBindPos)+"\n")
79 paramFile.write("\nBinding parameters\n")
80 paramFile.write("Bind time (s):\t"+str(sBindTime)+"\n")
82 paramFile.write("\nUnfolding parameters\n")
83 paramFile.write("Distance (nm):\t"+str(nmDist)+"\n")
84 paramFile.write("Step size (nm):\t"+str(nmStep)+"\n")
85 paramFile.write("Points per step:\t"+str(ppStep)+"\n")
86 paramFile.write("Unfold rate (nm/s):\t"+str(nmPsRate)+"\n")
87 paramFile.write("Sample rate (Hz):\t"+str(freq)+"\n")
89 paramFile.write("\nData fields:\tZ_piezo_out Deflection_in Z_piezo_in\n")
95 def __init__(self, controlTemp=True, dataDirectory=LOG_DIR) :
96 self.step = stepper.stepper_obj()
97 self.zp = z_piezo.z_piezo()
98 self.xp = x_piezo.x_piezo()
99 #self.zp = z_piezo.z_piezo(zp_chan = '/Dev1/ao0',
100 # zp_mon_chan = '/Dev1/ai1',
101 # def_chan = '/Dev1/ai0')
102 #self.xp = x_piezo.x_piezo(xp_chan = '/Dev1/ao1')
103 self.zp.jumpToPos(self.zp.pos_nm2out(0))
104 self.xp.jumpToPos(self.xp.nm2out(0))
105 if controlTemp == True :
106 self.T = temperature.tempController(maxCurrent=1.0)
108 self.log = unfold_data_log(dataDirectory, log_name="unfold")
109 def unfold(self, setpoint=None, rel_setpoint=1.0, sBindTime = 10.0,
110 nmDist=600, nmStep=0.5, ppStep=10, nmPsRate=1000,
111 dataDirectory=LOG_DIR, fileID=None) :
114 data = self.unfold_cycle(setpoint, rel_setpoint, sBindTime,
115 nmDist, nmStep, ppStep, nmPsRate,
116 dataDirectory, fileID)
118 except ExceptionTooFar :
119 EFILE.write('caught ExceptionTooFar\n'); EFILE.flush()
120 try : # try for a useful surface distance...
121 if setpoint == None : # HACK! redundant!
122 assert rel_setpoint != None, "must have some sort of setpoint"
123 setpoint = self.curDef() + rel_setpoint
124 print "setpoint = %g" % setpoint
125 EFILE.write('attempt getSurfPos\n'); EFILE.flush()
126 surfPos = self.getSurfPos(setpoint)
127 EFILE.write('getSurfPos succeeded\n'); EFILE.flush()
128 print "Too far (surface at %g nm), stepping closer" % surfPos
129 except z_piezo_utils.poorFit, string : # ... oh well, print what we know
130 EFILE.write('getSurfPos failed\n'); EFILE.flush()
131 print "Too far, stepping closer"
132 print "(Fit failed with: %s)" % string
133 EFILE.write('zero Z piezo\n'); EFILE.flush()
134 self.zp.jumpToPos(self.zp.pos_nm2out(0))
135 EFILE.write('step closer\n'); EFILE.flush()
137 EFILE.write('step closer successful\n'); EFILE.flush()
138 except ExceptionTooClose :
139 try : # try for a useful surface distance...
140 if setpoint == None : # !HACK !redundant!
141 assert rel_setpoint != None, "must have some sort of setpoint"
142 setpoint = self.curDef() + rel_setpoint
143 print "setpoint = %g" % setpoint
144 surfPos = self.getSurfPos(setpoint)
145 print "Too close (surface at %g nm), stepping away" % surfPos
146 except z_piezo_utils.poorFit, string : # ... oh well, print what we know
147 print "Too close, stepping away"
148 print "(Fit failed with: %s)" % string
149 self.zp.jumpToPos(self.zp.pos_nm2out(0))
151 print "Too close, stepping away"
153 def stepApproach(self, setpoint) :
155 while cd < setpoint or isnan(cd) : # sometimes during approach, we get negative nan values
156 print "deflection %g < setpoint %g. step closer" % (cd, setpoint)
159 def stepApproachRetractCurve(self, setpoint) :
162 orig_step = self.step.get_cur_pos()
166 step_array.append(self.step.get_cur_pos())
169 while cd < setpoint or isnan(cd) : # sometimes during approach, we get negative nan values
170 print "deflection %g < setpoint %g. step closer" % (cd, setpoint)
171 self.step.step_rel(2)
173 while self.step.get_cur_pos() > orig_step :
174 self.step.step_rel(-2)
176 return {"Deflection":def_array, "Stepper position":step_array}
177 def piezoApproach(self, setpoint, return_data=False) :
178 startPos = self.zp.curPos()
179 curVals,data = z_piezo_utils.moveToPosOrDef(self.zp,
181 self.zp.def_V2in(setpoint),
183 return_data=return_data)
184 if curVals["Deflection input"] < self.zp.def_V2in(setpoint) :
185 EFILE.write('Unfolding too far\n'); EFILE.flush()
186 self.zp.jumpToPos(startPos)
187 self.zp.updateInputs()
188 if PYLAB_INTERACTIVE_VERBOSE == True :
189 figure(BASE_FIG_NUM+1)
191 plot_dict(data, 'Approach')
193 title('Unfolding too far')
194 EFILE.write('Raising ExceptionTooFar\n'); EFILE.flush()
195 raise ExceptionTooFar
196 if return_data == True :
197 return (curVals, data)
200 def bind(self, sTime=10.0) :
202 def unfold_pull(self, nmDist=600, nmStep=0.5, ppStep=10, nmPsRate=1000) :
205 numSteps = int(nmDist/nmStep+1)
206 out = [0.0]*numSteps*ppStep
207 pos = self.zp.curPos()
209 step = int(self.zp.pos_nm2out(nmStep)-self.zp.pos_nm2out(0))
210 for i in range(0, numSteps) :
211 for j in range(0, ppStep) :
212 out[i*ppStep + j] = pos
214 # points/step * step/nm * nm/s = points/s
215 freq = ppStep / nmStep * nmPsRate
216 print "unfolding %d points per %d steps from %d to %d at freq of %g" % (ppStep, numSteps, startPos, pos, freq)
217 return {'freq':freq, 'data':self.zp.ramp(out, freq)}
218 def generate_refold_bind_pull_output(self, nmSurfPos=0,
219 sBindTime=2, nmUnfoldDist=160, nmRefoldDist=145, sPauseTime=2,
220 cycles=50, nmStep=0.5, ppStepUnfold=10, ppStepRefold=1,
221 nmPsRateUnfold=1000) :
225 numSteps = int(nmDist/nmStep+1)
226 out = [0.0]*numSteps*ppStep
227 pos = self.zp.curPos()
229 step = int(self.zp.pos_nm2out(nmStep)-self.zp.pos_nm2out(0))
230 for i in range(0, numSteps) :
231 for j in range(0, ppStep) :
232 out[i*ppStep + j] = pos
234 # points/step * step/nm * nm/s = points/s
235 freq = ppStep / nmStep * nmPsRate
236 print "unfolding %d points per %d steps from %d to %d at freq of %g" % (ppStep, numSteps, startPos, pos, freq)
237 return {'freq':freq, 'data':self.zp.ramp(out, freq)}
238 def unfold_cycle(self, setpoint=None, rel_setpoint=1.0, sBindTime = 10.0,
239 nmDist=600, nmStep=0.5, ppStep=10, nmPsRate=1000,
240 dataDirectory=LOG_DIR, fileID=None) :
241 if setpoint == None :
242 assert rel_setpoint != None, "must have some sort of setpoint"
243 setpoint = self.curDef() + rel_setpoint
244 print "setpoint = %g" % setpoint
245 timestamp = time.strftime("%Y%m%d%H%M%S")
247 if self.T != None : temp = self.T.getTemp()
249 startPos = self.zp.curPos()
250 curVals,approach_data = self.piezoApproach(setpoint, return_data=True)
251 bindPos = curVals['Z piezo output'] # in output units
252 finalPos = bindPos - (self.zp.pos_nm2out(nmDist)-self.zp.pos_nm2out(0))
254 self.zp._check_range(finalPos)
255 except z_piezo.outOfRange :
256 self.zp.jumpToPos(startPos)
257 self.zp.updateInputs()
258 if PYLAB_INTERACTIVE_VERBOSE == True :
259 figure(BASE_FIG_NUM+1)
261 plot_dict(approach_data, 'Approach')
263 title('Unfolding too close')
264 raise ExceptionTooClose
265 print "binding for %.3f seconds" % sBindTime
267 out = self.unfold_pull(nmDist, nmStep, ppStep, nmPsRate)
269 if PYLAB_INTERACTIVE_VERBOSE == True :
272 plot_dict(approach_data, 'Approach')
274 plot_dict(out['data'], 'Unfold')
281 timestamp = self.log.save(self, timestamp, temp, setpoint,
282 int(self.zp.pos_out2nm(bindPos)), # don't need lots of precision...
284 nmDist, nmStep, ppStep, nmPsRate, out['freq'],
285 approach_data, out['data'])
287 def getSurfPos(self, setpoint=2, textVerbose=False, plotVerbose=False) :
288 return self.zp.pos_out2nm(z_piezo_utils.getSurfPos(self.zp, self.zp.def_V2in(setpoint), textVerbose, plotVerbose))
290 self.zp.updateInputs()
291 return self.zp.def_in2V(self.zp.curDef())
292 def stepCloser(self) :
293 "Backlash-robust stepping"
294 self.step.step_rel(2) # two half-steps in full step mode
296 "Backlash-robust stepping"
297 self.step.step_rel(-120)
298 self.step.step_rel(110) # HACK, should come back 118
302 def _test_unfold(controlTemp=True) :
304 u = unfold(controlTemp=controlTemp)
305 u.unfold(setpoint=0.5, sBindTime=1.0)
307 print "unfold successful\n"
311 def __init__(self, controlTemp=True) :
312 self.u = unfold(controlTemp=controlTemp)
313 self.runUnfolds = False
315 self.getExternalTempSetpoint = None
316 if self.u.T != None :
317 self.tempSetpoint = self.u.T.getTemp()
318 self.getExternalDeflectionSetpoint = None
319 self.deflectionSetpoint = 0.5
320 self.getExternalBindTime = None
322 self.getExternalnmDist = None
324 self.getExternalnmStep = None
326 self.getExternalppStep = None
328 self.getExternalnmPsRate = None
329 self.nmPsRate = 1000.0
330 self.getExternalDataDirectory = None
331 self.dataDirectory = LOG_DIR
334 self.showUnfoldingIndex = None
337 if self.bgRun != None :
338 raise Exception, "Can't run two backgrounds simultaneously"
339 self.runUnfolds = True
340 print "unfolding in the background"
341 self.bgRun = bg_run( self._run)
342 def stepApproach(self) :
344 if self.getExternalDeflectionSetpoint != None :
345 self.deflectionSetpoint = self.getExternalDeflectionSetpoint()
346 self.u.stepApproach(self.deflectionSetpoint)
348 print "starting unfold loop"
349 while self.runUnfolds == True :
350 print "get unfold parameters"
351 if self.u.T != None and self.getExternalTempSetpoint != None :
352 setpoint = self.getExternalTempSetpoint()
353 if setpoint != self.tempSetpoint :
354 self.u.T.setTemp(setpoint)
355 self.tempSetpoint = setpoint
356 if self.getExternalDeflectionSetpoint != None :
357 self.deflectionSetpoint = self.getExternalDeflectionSetpoint()
358 if self.getExternalBindTime != None :
359 self.bindTime = self.getExternalBindTime()
360 if self.getExternalnmDist != None :
361 self.nmDist = self.getExternalnmDist()
362 if self.getExternalnmStep != None :
363 self.nmStep = self.getExternalnmStep()
364 if self.getExternalppStep != None :
365 self.ppStep = self.getExternalppStep()
366 if self.getExternalnmPsRate != None :
367 self.nmPsRate = self.getExternalnmPsRate()
368 if self.getExternalDataDirectory != None :
369 self.dataDirectory = self.getExternalDataDirectory()
371 #print "setpoint ", self.deflectionSetpoint
372 #print "bind time ", self.bindTime
373 #print "nmDist ", self.nmDist
374 #print "nmStep ", self.nmStep
375 #print "ppStep ", self.ppStep
376 #print "nmPsRate ", self.nmPsRate
377 #print "fileID ", self.i
378 #print "dataDir ", self.dataDirectory
379 data = self.u.unfold(setpoint=self.deflectionSetpoint,
380 sBindTime=self.bindTime,
384 nmPsRate=self.nmPsRate,
386 dataDirectory=self.dataDirectory)
388 if self.plotData != None :
389 self.plotData(data["data"]["Z piezo output"],
390 data["data"]["Deflection input"])
391 if self.showUnfoldingIndex != None :
392 self.showUnfoldingIndex(self.i)
396 if self.bgRun != None :
397 self.runUnfolds = False
401 def _test_unfold_expt(controlTemp=True) :
402 print "Test unfold_expt"
403 u_expt = unfold.unfold_expt(controlTemp=controlTemp)
407 print "unfold_expt working\n"
410 def __init__(self, function, args=None, finishCallback=None, interruptCallback=None) :
413 print "temp fn started"
416 complete = function()
418 complete = function(args)
419 print "temp Fn finished"
420 if finishCallback != None and complete == True :
422 elif interruptCallback != None and complete == False :
424 print "tempFn defined"
425 self.thd = threading.Thread(target=tempFn, name="Background Fn")
426 print "starting thread"
428 print "thread started"
430 print "joining thread"
432 print "thread joined"
436 print "test without args or callbacks"
442 bg = unfold.bg_run(say_hello)
444 print "The main thread's still going"
446 print "The background process is done"
448 print "test without args, but with callbacks"
450 print "I was interrupted"
453 bg = unfold.bg_run(say_hello, finishCallback=fin, interruptCallback=inter)
455 print "The main thread's still going"
457 print "The background process is done"
459 print "test with args and callbacks"
461 def say_hello(stop=[False]) :
463 if stop[0] == True : return False
468 bg = unfold.bg_run(say_hello, args=stop, finishCallback=fin, interruptCallback=inter)
470 print "The main thread's still going, stop it"
478 print "The background process is done"
480 print "bg_run working\n"
482 def loop_rates(u, rates, num_loops=10, die_file=None, song=None, **kwargs):
484 loop_rates(u, rates, num_loops=10, die_file=None, song=None, **kwargs)
486 Constant speed unfolding using the unfold instance u for num_loops
487 through the series of rates listed in rates. You may set die_file
488 to a path, loop_rates() will check that location before each
489 unfolding attempt, and cleanly stop unfolding if the file exists.
490 **kwargs are passed on to u.unfold(), so a full call might look like
492 loop_rates(u, rates=[20,200,2e3], num_loops=5, die_file='~/die', song='~wking/Music/system/towerclo.wav', rel_setpoint=1, nmDist=800, sBindTime=2)
495 die_file = os.path.expanduser(die_file)
497 song = os.path.expanduser(song)
498 for i in range(num_loops):
499 for nmPsRate in rates:
500 if die_file != None and os.path.exists(die_file):
502 u.unfold(nmPsRate=nmPsRate, **kwargs)
505 os.system("aplay '%s'" % song)
508 _test_unfold(controlTemp=False)
510 _test_unfold_expt(controlTemp=False)
512 if __name__ == "__main__" :