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
17 # TODO: Insert variables for calibration purposes here.
19 "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)
20 "DAC_Counts" : 2**12, # Maxed counts on DAC channel (12 bits = 2**12 = 4096)
22 # Calibration data for DAC and ADC
24 "ADC" : [None, None, None, None, None, None, None, None],
26 # Data used for rough calibration if above data is not present
27 "Vref" : 3.3, # The output of the voltage regulator (Vref = Vcc) relative to signal ground
28 "ADC_Rin" : [None, None, None, None, 15000, 1100, 1100, 1100],
29 "ADC_Rvar" : [None, None, None, None, 1000, 5000, 10000, 50000],
33 # TODO: Adjust aqcuisition parameters here
34 aquire = { "DAC_Sweep" : "0.0 + 250.0*int(step/200)", # DAC Sweep value (t is in STEPS, not seconds!)
36 "ADC_Vi" : 5, # ADC channel to read back Vi (set by DAC) through
37 "ADC_Is" : 4, # ADC channel to read back Is through
38 "ADC_Ie" : 4, # ADC channel to read back Ie through
39 "DAC_Settle" : 0.0, # Time in seconds to wait for DAC to stabilise
40 #"response_wait" : 0.2, # Time to wait in seconds between sending data and reading back
44 #Setup the serial connection parameters
46 port="/dev/ttyUSB0", # Modify as needed (note: in linux need to run `sudo chmod a+rw /dev/ttyUSBX' to set permissions)
48 # Do not change the values below here (unless AVR butterfly is reprogrammed to use different values)
59 "Accelerating Voltage" : None,
60 "Focus Voltage" : None,
61 "Deflection Voltage" : None,
62 "Venault Voltage" : None,
63 "Initial Voltage" : None,
64 "Heating Current" : None,
65 "Heating Voltage" : None,
66 "Chamber Pressure" : None,
67 "602 0.1 Battery" : None,
68 "602 0.03 Battery" : None,
69 "602 0.01 Battery" : None,
70 "602 0.003 Battery" : None,
71 "602 0.001 Battery" : None
75 return str(datetime.datetime.now()).split(" ")[1].split(".")[0].replace(":","")
78 return str(datetime.datetime.now()).split(" ")[0]
83 aquire["start_date"] = getDate()
90 # print("Waiting for \"# hello\" from device...")
91 # while (ser.readline().strip("\r\n") != "# hello"):
93 #while (ser.readline().strip("\r\n ") != "#"):
97 ser.write("a "+str(aquire["ADC_Averages"]) + "\r\n")
98 print(ser.readline().strip("\r\n"))
99 print(ser.readline().strip("\r\n"))
100 print(ser.readline().strip("\r\n"))
102 print("Writing config information to config.dat...")
103 output = open("config.dat", "w", 1)
105 output.write("# Initialise " + str(datetime.datetime.now()) + "\n")
107 for field in calibrate:
108 output.write("# calibrate["+str(field)+"] = "+str(calibrate[field]) + "\n")
111 output.write("# aquire["+str(field)+"] = "+str(aquire[field]) + "\n")
113 output.write("# Ready " + str(datetime.datetime.now()) + "\n# EOF\n")
120 if (loadCalibration_DAC() == False):
121 if (calibrateDAC() == False):
123 if (loadCalibration_ADC(aquire["ADC_Is"]) == False):
124 if (calibrateADC_usingDAC(aquire["ADC_Is"], False) == False):
125 if (calibrateADC(aquire["ADC_Is"]) == False):
128 if (loadCalibration_ADC(aquire["ADC_Vi"]) == False):
129 if (calibrateADC_usingDAC(aquire["ADC_Vi"], True) == False):
130 if (calibrateADC(aquire["ADC_Vi"]) == False):
134 # Make directory for today, backup calibration files
135 os.system("mkdir -p " + getDate())
136 os.system("cp *.dat " + getDate() +"/")
145 # TODO: Modify data to record here
147 #record_data([4, 5], getDate()+"/"+str(getTime())+".dat", None, None, "Measure emission&sample current varying with time, constant initial energy.")
149 os.system("mkdir -p " + getDate())
150 record_data([4, 5], getDate()+"/"+str(getTime())+".dat", None, 2500, "Measure deflection plate. lens with DAC. Sweep" + str(sweep) + " (started on " + aquire["start_date"]+")")
156 output = open(getDate()+"/checklist", "w", 0)
157 for item in parameters:
158 sys.stdout.write("Enter value for \""+str(item)+"\": ")
159 parameters[item] = sys.stdin.readline().strip("\r\n ")
160 sys.stdout.write("\n")
161 output.write(str(parameters[item]) + "\n")
164 def record_data(ADC_channels, output, pollTime = None, dac_max = None, comment = None):
166 output = [open(output, "w", 0), sys.stdout]
167 if (comment == None):
168 print("Enter a comment for the experiment.")
169 comment = sys.stdin.readline().strip("\r\n ")
170 output[0].write("# Comment: "+str(comment)+"\n")
172 output = [sys.stdout]
174 start_time = time.time()
177 out.write("# Experiment " + str(datetime.datetime.now()) + "\n")
178 out.write("# Polling for " + str(pollTime) + "s.\n")
179 out.write("# DAC = " + str(aquire["DAC_Sweep"]) + "\n")
180 out.write("# Data:\n")
181 out.write("# time\tDAC")
182 for channel in ADC_channels:
183 out.write("\tADC"+str(channel))
187 dacValue = int(eval(aquire["DAC_Sweep"]))
188 if (setDAC(dacValue) == False):
190 while (pollTime == None or time.time() < start_time + pollTime):
191 if (aquire["DAC_Sweep"] != None):
192 nextDacValue = int(eval(aquire["DAC_Sweep"]))
193 if (nextDacValue != dacValue):
194 dacValue = nextDacValue
201 if (dac_max != None and dacValue >= dac_max):
204 measure_start = time.time()
208 for channel in ADC_channels:
209 read = readADC(channel)
211 print("Abort data collection")
213 raw_adc.append((channel, read[0], read[1]))
215 end_time = time.time()
218 out.write(str((measure_start + (end_time - measure_start)/2.0 - start_time)))
219 out.write("\t"+str(dacValue))
221 out.write("\t" + str(adc[1]) + "\t" + str(adc[2]))
226 if out != sys.stdout:
230 def loadCalibration_ADC(channel):
232 input_file = open("calibrateADC"+str(channel)+".dat")
234 print("Couldn't find calibration file for ADC " + str(channel))
237 calibrate["ADC"][channel] = []
239 l = l.split("#")[0].strip("\r\n ")
243 split_line = l.split("\t")
244 calibrate["ADC"][channel].append((float(split_line[0]), float(split_line[1])))
247 if (len(calibrate["ADC"][channel]) <= 0):
248 print("Empty calibration file for ADC " + str(channel))
252 def loadCalibration_DAC():
254 input_file = open("calibrateDAC.dat")
256 print("Couldn't find calibration file for DAC")
259 calibrate["DAC"] = []
261 #print("Line is: "+str(l))
262 l = l.split("#")[0].strip("\r\n ")
266 split_line = l.split("\t")
267 if (len(split_line) >= 3):
268 calibrate["DAC"].append((int(split_line[0]), float(split_line[1]), float(split_line[2])))
270 calibrate["DAC"].append((int(split_line[0]), float(split_line[1])))
275 if (len(calibrate["DAC"]) <= 0):
276 print("Empty calibration file for DAC")
280 def getADC_Voltage(channel, counts):
281 if (calibrate["ADC"][channel] == None or len(calibrate["ADC"][channel]) <= 0):
282 if (calibrate["ADC_Rin"][channel] != None and calibrate["ADC_Rvar"][channel] != None):
283 print("Warning: Using rough calibration for ADC"+str(channel) + " = " + str(counts))
284 ratio = float(calibrate["ADC_Rin"][channel]) / (float(calibrate["ADC_Rin"][channel]) + float(calibrate["ADC_Rvar"][channel]))
285 return ratio * (float(counts) / float(calibrate["ADC_Counts"][channel]) * Vref)
287 print("Error: No calibration for ADC"+str(channel))
290 c = calibrate["ADC"][channel]
292 for i in range(0, len(c)-1):
293 if (c[i][0] <= counts and i + 1 < len(c)):
294 grad = (float(c[i+1][valueIndex]) - float(c[i][valueIndex])) / (float(c[i+1][0]) - float(c[i][0]))
295 value = float(c[i][valueIndex]) + grad * (float(counts) - float(c[i][0]))
299 print("Warning: Extrapolating outside calibration range for DAC = " + str(counts))
301 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]))
302 value = float(c[len(c)-1][valueIndex]) + grad * (float(counts) - float(c[len(c)-1][0]))
304 def readADC(channel):
305 #for i in range(0, aquire["ADC_Averages"]):
306 # ser.write("r "+str(channel)+"\r") # Send command to datalogger
307 #time.sleep(aquire["response_wait"])
308 # response = ser.readline().strip("#\r\n ")
309 # if (response != "r "+str(channel)):
310 # print("Received wierd response reading ADC ("+response+")")
312 #time.sleep(aquire["response_wait"])
313 # values.append(int(ser.readline().strip("\r\n ")))
314 # adc_sum += float(values[len(values)-1])
315 ser.write("r "+str(channel)+"\r")
316 response = ser.readline().strip("#\r\n")
317 if (response != "r "+str(channel)):
318 print("Received wierd response reading ADC ("+response+")")
320 return ser.readline().strip("\r\n").split(" ")
322 def getDAC_Voltage(counts, gain = True):
323 if (calibrate["DAC"] == None or len(calibrate["DAC"]) <= 0):
324 if (calibrate["DAC_Gain"] != None):
325 print("Warning: Using rough calibration for DAC = " + str(counts))
326 return float(counts) / float(calibrate["DAC_Counts"]) * float(calibrate["Vref"]) * float(calibrate["DAC_Gain"])
328 print("Error: No calibrate for DAC")
334 if (len(calibrate["DAC"][0]) < 3):
335 print("Error: No data for unamplified DAC")
340 print("Warning: Only one point in calibration data!")
341 return float(c[0][valueIndex])
343 for i in range(0, len(c)-1):
344 if (c[i][0] <= counts and i + 1 < len(c)):
345 grad = (float(c[i+1][valueIndex]) - float(c[i][valueIndex])) / (float(c[i+1][0]) - float(c[i][0]))
346 value = float(c[i][valueIndex]) + grad * (float(counts) - float(c[i][0]))
350 print("Warning: Extrapolating outside calibration range for DAC = " + str(counts))
352 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]))
353 value = float(c[len(c)-1][valueIndex]) + grad * (float(counts) - float(c[len(c)-1][0]))
359 ser.write("d "+str(level)+"\r")
361 response = ser.readline().strip("#\r\n ")
362 if (response != "d "+str(level)):
363 print("Recieved wierd response setting DAC to "+str(level) + " ("+response+")")
365 #time.sleep(aquire["response_wait"])
366 #time.sleep(aquire["DAC_Settle"])
367 #time.sleep(aquire["DAC_Settle"])
368 response = ser.readline().strip("#\r\n ")
369 if (response != "DAC "+str(level)):
370 print("Recieved wierd response setting DAC to "+str(level) + " ("+response+")")
372 #time.sleep(aquire["response_wait"])
373 time.sleep(aquire["DAC_Settle"])
376 def calibrateADC_usingDAC(channel, gain = True):
377 if (calibrate["DAC"] == None):
378 print("ERROR: DAC is not calibrated, can't use to calibrate ADC!")
381 calibrate["ADC"][channel] = []
382 outfile = open("calibrateADC"+str(channel)+".dat", "w", 1)
383 outfile.write("# Calibrate ADC " + str(channel) + "\n")
384 outfile.write("# Start " + str(datetime.datetime.now()) + "\n")
386 print("Connect DAC output to ADC " + str(channel) + " and press enter\n")
389 for dac in calibrate["DAC"]:
390 if (setDAC(dac[0]) == False):
393 value = readADC(channel)
396 canadd = (len(calibrate["ADC"][channel]) <= 0)
397 if (canadd == False):
399 for adc in calibrate["ADC"][channel]:
400 if adc[0] == value[0]:
411 #input_value = getDAC_Voltage(dac[0], gain)
412 if (input_value == False):
415 outfile.write(str(value[0]) + "\t" + str(input_value) + "\t" + str(value[1]) + "\n")
416 print(str(value[0]) + "\t" + str(input_value) + "\t" + str(value[1]))
418 calibrate["ADC"][channel].append((value[0], input_value, value[1]))
420 outfile.write("# Stop " + str(datetime.datetime.now()) + "\n# EOF\n")
422 if (setDAC(0) == False):
424 if (len(calibrate["ADC"][channel]) <= 0):
425 print("Error: No calibration points taken for ADC " + str(channel))
429 def calibrateADC(channel):
430 calibrate["ADC"][channel] = []
431 outfile = open("calibrateADC"+str(channel)+".dat", "w", 1)
432 outfile.write("# Calibrate ADC " + str(channel) + "\n")
433 outfile.write("# Start " + str(datetime.datetime.now()) + "\n")
435 print("Calibrating ADC...\n")
436 print("Enter measured voltage, empty line stops.\n")
439 read = sys.stdin.readline().strip("\r\n ")
442 input_value = float(read)
443 output_value = readADC(channel)
444 if (output_value == False):
447 calibrate["ADC"][channel].append((output_value[0], input_value))
448 print(str(output_value[0]) + "\t" + str(input_value))
450 calibrate["ADC"][channel].sort(lambda e : e[1], reverse = False)
455 outfile.write("# Stop " + str(datetime.datetime.now()) + "\n# EOF\n")
457 if (len(calibrate["ADC"][channel]) <= 0):
458 print("Error: No calibration points taken for ADC " + str(channel))
464 calibrate["DAC"] = []
466 outfile = open("calibrateDAC.dat", "w", 1)
467 outfile.write("# Calibrate DAC\n")
468 outfile.write("# Start " + str(datetime.datetime.now()) + "\n")
470 print("Calibrating DAC...")
471 sys.stdout.write("Number of counts to increment per step: ")
472 read = sys.stdin.readline().strip("\r\n")
475 stepSize = max(1, int(read))
476 sys.stdout.write("\n")
478 outfile.write("# Increment by " + str(stepSize) + "\n")
479 print("Input measured DAC voltage for each level; empty input ends calibration.")
480 print("You may optionally input the DAC voltage before it is amplified too.")
483 while (level < calibrate["DAC_Counts"]):
487 sys.stdout.write(str(level) + " ?")
488 read = sys.stdin.readline().strip("\r\n ")
492 read = read.split(" ")
493 if (len(calibrate["DAC"]) > 0 and len(calibrate["DAC"][len(calibrate["DAC"])-1]) != len(read)):
494 print("Either give one value for EVERY point, or two values for EVERY point. Don't mess around.")
497 calibrate["DAC"].append((level, float(read[0]), float(read[1])))
498 outfile.write(str(level) + "\t" + str(float(read[0])) + "\t" + str(float(read[1])) + "\n")
500 calibrate["DAC"].append((level, float(read[0])))
501 outfile.write(str(level) + "\t" + str(float(read[0])) + "\n")
507 outfile.write("# Stop " + str(datetime.datetime.now()) + "\n# EOF\n")
509 if (len(calibrate["DAC"]) <= 0):
510 print("Error: No calibration points taken for DAC")
514 # Run the main function
515 if __name__ == "__main__":