5 # @purpose AVR Butterfly Datalogger control for Total Current Spectroscopy Measurements
6 # The AVR responds to a limited number of RS232 commands to read ADC channels and set the DAC
7 # This script can be used to control the DAC and monitor initial energy & sample current over RS232
18 # TODO: Insert variables for calibration purposes here.
20 "ADC_Counts" : [2**10,2**10,2**10,2**10,2**10,2**10,2**10], # Maxed counts on ADC channels (10 bits = 2**10 = 1024)
21 "DAC_Counts" : 2**12, # Maxed counts on DAC channel (12 bits = 2**12 = 4096)
23 # Calibration data for DAC and ADC
25 "ADC" : [None, None, None, None, None, None, None, None],
27 # Data used for rough calibration if above data is not present
28 "Vref" : 3.3, # The output of the voltage regulator (Vref = Vcc) relative to signal ground
29 "ADC_Rin" : [None, None, None, None, 15000, 1100, 1100, 1100],
30 "ADC_Rvar" : [None, None, None, None, 1000, 5000, 10000, 50000],
34 # TODO: Adjust aqcuisition parameters here
35 aquire = { "DAC_Sweep" : "0.0 + 50.0*int(step/60)", # DAC Sweep value (t is in STEPS, not seconds!)
37 "ADC_Vi" : 5, # ADC channel to read back Vi (set by DAC) through
38 "ADC_Is" : 4, # ADC channel to read back Is through
39 "ADC_Ie" : 4, # ADC channel to read back Ie through
40 "DAC_Settle" : 0.0, # Time in seconds to wait for DAC to stabilise
41 #"response_wait" : 0.2, # Time to wait in seconds between sending data and reading back
46 #Setup the serial connection parameters
48 port="/dev/ttyUSB0", # Modify as needed (note: in linux need to run `sudo chmod a+rw /dev/ttyUSBX' to set permissions)
50 # Do not change the values below here (unless AVR butterfly is reprogrammed to use different values)
60 parameters = OrderedDict([
61 ("Accelerating Voltage" , None),
62 ("Focus Voltage" , None),
63 ("Deflection Voltage" , None),
64 ("Venault Voltage" , None),
65 ("Initial Voltage" , None),
66 ("Heating Current" , None),
67 ("Heating Voltage" , None),
68 ("Chamber Pressure" , None),
71 ("610B Scale" , None),
73 ("602 0.1 Battery" , None),
74 ("602 0.03 Battery" , None),
75 ("602 0.01 Battery" , None),
76 ("602 0.003 Battery" , None),
77 ("602 0.001 Battery" , None),
78 ("ADC Battery" , None),
79 ("ADC Regulator" , None),
86 return str(datetime.datetime.now()).split(" ")[1].split(".")[0].replace(":","")
89 return str(datetime.datetime.now()).split(" ")[0]
91 # Used for when I press Control-C to stop things
92 def set_exit_handler(func):
96 win32api.SetConsoleCtrlHandler(func, True)
98 version = “.”.join(map(str, sys.version_info[:2]))
99 raise Exception(”pywin32 not installed for Python ” + version)
102 signal.signal(signal.SIGTERM, func)
110 aquire["start_date"] = getDate()
117 # print("Waiting for \"# hello\" from device...")
118 # while (ser.readline().strip("\r\n") != "# hello"):
120 #while (ser.readline().strip("\r\n ") != "#"):
124 ser.write("a "+str(aquire["ADC_Averages"]) + "\r\n")
125 print(ser.readline().strip("\r\n"))
126 print(ser.readline().strip("\r\n"))
127 print(ser.readline().strip("\r\n"))
129 print("Writing config information to config.dat...")
130 output = open("config.dat", "w", 1)
132 output.write("# Initialise " + str(datetime.datetime.now()) + "\n")
134 #for field in calibrate:
135 # output.write("# calibrate["+str(field)+"] = "+str(calibrate[field]) + "\n")
138 output.write("# aquire["+str(field)+"] = "+str(aquire[field]) + "\n")
140 output.write("# Ready " + str(datetime.datetime.now()) + "\n# EOF\n")
147 # I haven't ever used calibrated results, and yet this code is still here, why???
148 #if (loadCalibration_DAC() == False):
149 # if (calibrateDAC() == False):
151 #if (loadCalibration_ADC(aquire["ADC_Is"]) == False):
152 # if (calibrateADC_usingDAC(aquire["ADC_Is"], False) == False):
153 # if (calibrateADC(aquire["ADC_Is"]) == False):
156 #if (loadCalibration_ADC(aquire["ADC_Vi"]) == False):
157 # if (calibrateADC_usingDAC(aquire["ADC_Vi"], True) == False):
158 # if (calibrateADC(aquire["ADC_Vi"]) == False):
162 # Make directory for today, backup calibration files
163 os.system("mkdir -p " + getDate())
164 #os.system("cp *.dat " + getDate() +"/")
173 # TODO: Modify data to record here
176 os.system("mkdir -p " + getDate())
177 record_data([4, 5], getDate()+"/"+str(getTime())+".dat", None, 4000)
181 def checkList(output_file):
183 input_file = open(getDate()+"/checklist", "r")
187 if (input_file != None):
188 for line in input_file:
190 item = k[0].strip(" \r\n")
191 value = k[1].strip(" \r\n")
192 if (item in parameters):
193 parameters[item] = value
195 print("Checklist found. Overwrite? [Y/n]")
196 response = sys.stdin.readline().strip(" \r\n")
197 if (response == "" or response == "y" or response == "Y"):
200 if (input_file == None):
201 for item in parameters:
202 sys.stdout.write("\""+str(item)+"\" = " + str(parameters[item]) + " New value?: ")
203 response = sys.stdin.readline().strip("\r\n ")
205 parameters[item] = response
206 sys.stdout.write("\n")
209 checklist = open(getDate()+"/checklist", "w", 0)
210 for item in parameters:
211 checklist.write("# "+str(item) + " = " + str(parameters[item]) + "\n")
212 output_file.write("# "+str(item) + " = " + str(parameters[item]) + "\n")
216 def record_data(ADC_channels, output, pollTime = None, dac_max = None):
219 output = [open(output, "w", 0), sys.stdout]
222 output = [sys.stdout]
226 out.write("# aquire["+str(field)+"] = "+str(aquire[field]) + "\n")
229 out.write("# Checklist:\n")
231 for field in checklist:
233 out.write("# "+str(field)+" = " + str(checklist[field]) + "\n")
236 start_time = time.time()
240 out.write("# Experiment " + str(datetime.datetime.now()) + "\n")
241 out.write("# Polling for " + str(pollTime) + "s.\n")
243 out.write("# Data:\n")
244 out.write("# time\tDAC")
245 for channel in ADC_channels:
246 out.write("\tADC"+str(channel))
250 dacValue = int(eval(aquire["DAC_Sweep"]))
251 if (setDAC(dacValue) == False):
253 while (pollTime == None or time.time() < start_time + pollTime):
254 if (aquire["DAC_Sweep"] != None):
255 nextDacValue = int(eval(aquire["DAC_Sweep"]))
256 if (nextDacValue != dacValue):
257 dacValue = nextDacValue
264 if (dac_max != None and dacValue >= dac_max):
267 measure_start = time.time()
271 for channel in ADC_channels:
272 read = readADC(channel)
274 print("Abort data collection")
276 raw_adc.append((channel, read[0], read[1]))
278 end_time = time.time()
281 out.write(str((measure_start + (end_time - measure_start)/2.0 - start_time)))
282 out.write("\t"+str(dacValue))
284 out.write("\t" + str(adc[1]) + "\t" + str(adc[2]))
289 if out != sys.stdout:
293 def loadCalibration_ADC(channel):
295 input_file = open("calibrateADC"+str(channel)+".dat")
297 print("Couldn't find calibration file for ADC " + str(channel))
300 calibrate["ADC"][channel] = []
302 l = l.split("#")[0].strip("\r\n ")
306 split_line = l.split("\t")
307 calibrate["ADC"][channel].append((float(split_line[0]), float(split_line[1])))
310 if (len(calibrate["ADC"][channel]) <= 0):
311 print("Empty calibration file for ADC " + str(channel))
315 def loadCalibration_DAC():
317 input_file = open("calibrateDAC.dat")
319 print("Couldn't find calibration file for DAC")
322 calibrate["DAC"] = []
324 #print("Line is: "+str(l))
325 l = l.split("#")[0].strip("\r\n ")
329 split_line = l.split("\t")
330 if (len(split_line) >= 3):
331 calibrate["DAC"].append((int(split_line[0]), float(split_line[1]), float(split_line[2])))
333 calibrate["DAC"].append((int(split_line[0]), float(split_line[1])))
338 if (len(calibrate["DAC"]) <= 0):
339 print("Empty calibration file for DAC")
343 def getADC_Voltage(channel, counts):
344 if (calibrate["ADC"][channel] == None or len(calibrate["ADC"][channel]) <= 0):
345 if (calibrate["ADC_Rin"][channel] != None and calibrate["ADC_Rvar"][channel] != None):
346 print("Warning: Using rough calibration for ADC"+str(channel) + " = " + str(counts))
347 ratio = float(calibrate["ADC_Rin"][channel]) / (float(calibrate["ADC_Rin"][channel]) + float(calibrate["ADC_Rvar"][channel]))
348 return ratio * (float(counts) / float(calibrate["ADC_Counts"][channel]) * Vref)
350 print("Error: No calibration for ADC"+str(channel))
353 c = calibrate["ADC"][channel]
355 for i in range(0, len(c)-1):
356 if (c[i][0] <= counts and i + 1 < len(c)):
357 grad = (float(c[i+1][valueIndex]) - float(c[i][valueIndex])) / (float(c[i+1][0]) - float(c[i][0]))
358 value = float(c[i][valueIndex]) + grad * (float(counts) - float(c[i][0]))
362 print("Warning: Extrapolating outside calibration range for DAC = " + str(counts))
364 grad = (float(c[len(c)-1][valueIndex]) - float(c[len(c)-2][valueIndex])) / (float(c[len(c)-1][0]) - float(c[len(c)-2][0]))
365 value = float(c[len(c)-1][valueIndex]) + grad * (float(counts) - float(c[len(c)-1][0]))
367 def readADC(channel):
368 #for i in range(0, aquire["ADC_Averages"]):
369 # ser.write("r "+str(channel)+"\r") # Send command to datalogger
370 #time.sleep(aquire["response_wait"])
371 # response = ser.readline().strip("#\r\n ")
372 # if (response != "r "+str(channel)):
373 # print("Received wierd response reading ADC ("+response+")")
375 #time.sleep(aquire["response_wait"])
376 # values.append(int(ser.readline().strip("\r\n ")))
377 # adc_sum += float(values[len(values)-1])
378 ser.write("r "+str(channel)+"\r")
379 response = ser.readline().strip("#\r\n")
380 if (response != "r "+str(channel)):
381 print("Received wierd response reading ADC ("+response+")")
383 return ser.readline().strip("\r\n").split(" ")
385 def getDAC_Voltage(counts, gain = True):
386 if (calibrate["DAC"] == None or len(calibrate["DAC"]) <= 0):
387 if (calibrate["DAC_Gain"] != None):
388 print("Warning: Using rough calibration for DAC = " + str(counts))
389 return float(counts) / float(calibrate["DAC_Counts"]) * float(calibrate["Vref"]) * float(calibrate["DAC_Gain"])
391 print("Error: No calibrate for DAC")
397 if (len(calibrate["DAC"][0]) < 3):
398 print("Error: No data for unamplified DAC")
403 print("Warning: Only one point in calibration data!")
404 return float(c[0][valueIndex])
406 for i in range(0, len(c)-1):
407 if (c[i][0] <= counts and i + 1 < len(c)):
408 grad = (float(c[i+1][valueIndex]) - float(c[i][valueIndex])) / (float(c[i+1][0]) - float(c[i][0]))
409 value = float(c[i][valueIndex]) + grad * (float(counts) - float(c[i][0]))
413 print("Warning: Extrapolating outside calibration range for DAC = " + str(counts))
415 grad = (float(c[len(c)-1][valueIndex]) - float(c[len(c)-2][valueIndex])) / (float(c[len(c)-1][0]) - float(c[len(c)-2][0]))
416 value = float(c[len(c)-1][valueIndex]) + grad * (float(counts) - float(c[len(c)-1][0]))
422 ser.write("d "+str(level)+"\r")
424 response = ser.readline().strip("#\r\n ")
425 if (response != "d "+str(level)):
426 print("Recieved wierd response setting DAC to "+str(level) + " ("+response+")")
428 #time.sleep(aquire["response_wait"])
429 #time.sleep(aquire["DAC_Settle"])
430 #time.sleep(aquire["DAC_Settle"])
431 response = ser.readline().strip("#\r\n ")
432 if (response != "DAC "+str(level)):
433 print("Recieved wierd response setting DAC to "+str(level) + " ("+response+")")
435 #time.sleep(aquire["response_wait"])
436 time.sleep(aquire["DAC_Settle"])
439 def calibrateADC_usingDAC(channel, gain = True):
440 if (calibrate["DAC"] == None):
441 print("ERROR: DAC is not calibrated, can't use to calibrate ADC!")
444 calibrate["ADC"][channel] = []
445 outfile = open("calibrateADC"+str(channel)+".dat", "w", 1)
446 outfile.write("# Calibrate ADC " + str(channel) + "\n")
447 outfile.write("# Start " + str(datetime.datetime.now()) + "\n")
449 print("Connect DAC output to ADC " + str(channel) + " and press enter\n")
452 for dac in calibrate["DAC"]:
453 if (setDAC(dac[0]) == False):
456 value = readADC(channel)
459 canadd = (len(calibrate["ADC"][channel]) <= 0)
460 if (canadd == False):
462 for adc in calibrate["ADC"][channel]:
463 if adc[0] == value[0]:
474 #input_value = getDAC_Voltage(dac[0], gain)
475 if (input_value == False):
478 outfile.write(str(value[0]) + "\t" + str(input_value) + "\t" + str(value[1]) + "\n")
479 print(str(value[0]) + "\t" + str(input_value) + "\t" + str(value[1]))
481 calibrate["ADC"][channel].append((value[0], input_value, value[1]))
483 outfile.write("# Stop " + str(datetime.datetime.now()) + "\n# EOF\n")
485 if (setDAC(0) == False):
487 if (len(calibrate["ADC"][channel]) <= 0):
488 print("Error: No calibration points taken for ADC " + str(channel))
492 def calibrateADC(channel):
493 calibrate["ADC"][channel] = []
494 outfile = open("calibrateADC"+str(channel)+".dat", "w", 1)
495 outfile.write("# Calibrate ADC " + str(channel) + "\n")
496 outfile.write("# Start " + str(datetime.datetime.now()) + "\n")
498 print("Calibrating ADC...\n")
499 print("Enter measured voltage, empty line stops.\n")
502 read = sys.stdin.readline().strip("\r\n ")
505 input_value = float(read)
506 output_value = readADC(channel)
507 if (output_value == False):
510 calibrate["ADC"][channel].append((output_value[0], input_value))
511 print(str(output_value[0]) + "\t" + str(input_value))
513 calibrate["ADC"][channel].sort(lambda e : e[1], reverse = False)
518 outfile.write("# Stop " + str(datetime.datetime.now()) + "\n# EOF\n")
520 if (len(calibrate["ADC"][channel]) <= 0):
521 print("Error: No calibration points taken for ADC " + str(channel))
527 calibrate["DAC"] = []
529 outfile = open("calibrateDAC.dat", "w", 1)
530 outfile.write("# Calibrate DAC\n")
531 outfile.write("# Start " + str(datetime.datetime.now()) + "\n")
533 print("Calibrating DAC...")
534 sys.stdout.write("Number of counts to increment per step: ")
535 read = sys.stdin.readline().strip("\r\n")
538 stepSize = max(1, int(read))
539 sys.stdout.write("\n")
541 outfile.write("# Increment by " + str(stepSize) + "\n")
542 print("Input measured DAC voltage for each level; empty input ends calibration.")
543 print("You may optionally input the DAC voltage before it is amplified too.")
546 while (level < calibrate["DAC_Counts"]):
550 sys.stdout.write(str(level) + " ?")
551 read = sys.stdin.readline().strip("\r\n ")
555 read = read.split(" ")
556 if (len(calibrate["DAC"]) > 0 and len(calibrate["DAC"][len(calibrate["DAC"])-1]) != len(read)):
557 print("Either give one value for EVERY point, or two values for EVERY point. Don't mess around.")
560 calibrate["DAC"].append((level, float(read[0]), float(read[1])))
561 outfile.write(str(level) + "\t" + str(float(read[0])) + "\t" + str(float(read[1])) + "\n")
563 calibrate["DAC"].append((level, float(read[0])))
564 outfile.write(str(level) + "\t" + str(float(read[0])) + "\n")
570 outfile.write("# Stop " + str(datetime.datetime.now()) + "\n# EOF\n")
572 if (len(calibrate["DAC"]) <= 0):
573 print("Error: No calibration points taken for DAC")
577 # Run the main function
578 if __name__ == "__main__":