Automatic commit. Thu Aug 2 13:00:02 WST 2012
[matches/honours.git] / research / TCS / interface.py
1 #!/usr/bin/python
2
3 # @file "interface.py"
4 # @author Sam Moore
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
8
9 import sys
10 import os
11 import time
12 import serial
13 import datetime
14
15
16
17 # TODO: Insert variables for calibration purposes here.
18 calibrate = {
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)
21
22         # Calibration data for DAC and ADC
23         "DAC" : None, 
24         "ADC" : [None, None, None, None, None, None, None, None],
25
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],
30         "DAC_Gain" : 1
31 }
32
33 # TODO: Adjust aqcuisition parameters here
34 aquire = { "DAC_Sweep" : "0.0 + 250.0*int(step/600)", # DAC Sweep value (t is in STEPS, not seconds!)
35         "ADC_Averages" : 200,
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
41         "start_date" : None
42 }
43
44 #Setup the serial connection parameters
45 ser = serial.Serial(
46         port="/dev/ttyUSB0", # Modify as needed (note: in linux need to run `sudo chmod a+rw /dev/ttyUSBX' to set permissions)
47
48         # Do not change the values below here (unless AVR butterfly is reprogrammed to use different values)
49         baudrate=4800,
50         parity="N",
51         stopbits=1,
52         bytesize=8,
53         timeout=None,
54         xonxoff=0,
55         rtscts=0
56 )
57
58 parameters = {
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         "610B Zero" : None,
68         "602 Zero" : None,
69         "610B Scale" : None,
70         "602 Scale" : None,
71         "602 0.1 Battery" : None,
72         "602 0.03 Battery" : None,
73         "602 0.01 Battery" : None,
74         "602 0.003 Battery" : None,
75         "602 0.001 Battery" : None, 
76         "ADC Battery" : None,
77         "ADC Regulator" : None,
78         "Title" : None,
79         "Comment" : None,
80         "Data" : None,
81
82 }
83
84 def getTime():
85         return str(datetime.datetime.now()).split(" ")[1].split(".")[0].replace(":","")
86
87 def getDate():
88         return str(datetime.datetime.now()).split(" ")[0]
89
90 def init():
91
92         
93         aquire["start_date"] = getDate()
94
95
96
97         # Connect serial
98         ser.open()
99         ser.isOpen()
100 #       print("Waiting for \"# hello\" from device...")
101 #       while (ser.readline().strip("\r\n") != "# hello"):
102 #               pass
103         #while (ser.readline().strip("\r\n ") != "#"):
104         #       pass
105         time.sleep(1.0)
106
107         ser.write("a "+str(aquire["ADC_Averages"]) + "\r\n")
108         print(ser.readline().strip("\r\n"))
109         print(ser.readline().strip("\r\n"))
110         print(ser.readline().strip("\r\n"))
111
112         print("Writing config information to config.dat...")
113         output = open("config.dat", "w", 1)
114
115         output.write("# Initialise " + str(datetime.datetime.now()) + "\n")
116
117         for field in calibrate:
118                 output.write("# calibrate["+str(field)+"] = "+str(calibrate[field]) + "\n")
119         output.write("\n")
120         for field in aquire:
121                 output.write("# aquire["+str(field)+"] = "+str(aquire[field]) + "\n")
122
123         output.write("# Ready " + str(datetime.datetime.now()) + "\n# EOF\n")
124         output.close()
125
126 def main():
127
128         init()
129         
130         if (loadCalibration_DAC() == False):
131                 if (calibrateDAC() == False):
132                         return -1
133         if (loadCalibration_ADC(aquire["ADC_Is"]) == False):
134                 if (calibrateADC_usingDAC(aquire["ADC_Is"], False) == False):
135                         if (calibrateADC(aquire["ADC_Is"]) == False):
136                                 return -1
137
138         if (loadCalibration_ADC(aquire["ADC_Vi"]) == False):
139                 if (calibrateADC_usingDAC(aquire["ADC_Vi"], True) == False):
140                         if (calibrateADC(aquire["ADC_Vi"]) == False):
141                                 return -1
142         
143
144         # Make directory for today, backup calibration files
145         os.system("mkdir -p " + getDate())
146         os.system("cp *.dat " + getDate() +"/")
147
148         #checkList()
149
150         
151         
152                 
153
154         # Experiment
155         # TODO: Modify data to record here
156         sweep = 1
157         while True:
158                 os.system("mkdir -p " + getDate())
159                 record_data([4], getDate()+"/"+str(getTime())+".dat", None, 2500)
160                 sweep += 1
161         
162
163 def checkList(output_file):
164         try:
165                 input_file = open(getDate()+"/checklist", "r")
166         except:
167                 input_file = None
168
169         if (input_file != None):
170                 for line in input_file:
171                         k = line.split("=")
172                         item = k[0].strip(" \r\n")
173                         value = k[1].strip(" \r\n")
174                         if (item in parameters):
175                                 parameters[item] = value
176         
177                 print("Checklist found. Overwrite? [Y/n]")
178                 response = sys.stdin.readline().strip(" \r\n")
179                 if (response == "" or response == "y" or response == "Y"):
180                         input_file = None
181         
182         if (input_file == None):
183                 for item in parameters:
184                         sys.stdout.write("\""+str(item)+"\" = " + str(parameters[item]) + " New value?: ")
185                         response = sys.stdin.readline().strip("\r\n ")
186                         if (response != ""):
187                                 parameters[item] = response
188                         sys.stdout.write("\n")
189                         
190
191         checklist = open(getDate()+"/checklist", "w", 0)
192         for item in parameters:
193                 checklist.write("# "+str(item) + " = " + str(parameters[item]) + "\n")
194                 output_file.write("# "+str(item) + " = " + str(parameters[item]) + "\n")
195
196
197
198 def record_data(ADC_channels, output, pollTime = None, dac_max = None):
199         
200         if (output != None):
201                 output = [open(output, "w", 0), sys.stdout]
202                 checkList(output[0])
203         else:
204                 output = [sys.stdout]
205
206         start_time = time.time()
207         
208         for out in output:
209                 out.write("# Experiment " + str(datetime.datetime.now()) + "\n")
210                 out.write("# Polling for " + str(pollTime) + "s.\n")
211                 out.write("# DAC = " + str(aquire["DAC_Sweep"]) + "\n")
212                 out.write("# Data:\n")
213                 out.write("# time\tDAC")
214                 for channel in ADC_channels:
215                         out.write("\tADC"+str(channel))
216                 out.write("\n")
217
218         step = 0
219         dacValue = int(eval(aquire["DAC_Sweep"]))
220         if (setDAC(dacValue) == False):
221                 setDAC(dacValue)
222         while (pollTime == None or time.time() < start_time + pollTime):
223                 if (aquire["DAC_Sweep"] != None):
224                         nextDacValue = int(eval(aquire["DAC_Sweep"]))
225                         if (nextDacValue != dacValue):
226                                 dacValue = nextDacValue
227                                 if (dacValue < 0):
228                                         break
229                                 setDAC(dacValue)
230                         step += 1
231                 
232
233                 if (dac_max != None and dacValue >= dac_max):
234                         break
235
236                 measure_start = time.time()
237
238                 raw_adc = []
239
240                 for channel in ADC_channels:
241                         read = readADC(channel)
242                         if read == False:
243                                 print("Abort data collection")
244                                 return False
245                         raw_adc.append((channel, read[0], read[1]))
246
247                 end_time = time.time()
248
249                 for out in output:
250                         out.write(str((measure_start + (end_time - measure_start)/2.0 - start_time)))
251                         out.write("\t"+str(dacValue))
252                         for adc in raw_adc:
253                                 out.write("\t" + str(adc[1]) + "\t" + str(adc[2]))
254                         out.write("\n") 
255         
256                 
257         for out in output:              
258                 if out != sys.stdout:
259                         out.close()
260         return True
261
262 def loadCalibration_ADC(channel):
263         try:
264                 input_file = open("calibrateADC"+str(channel)+".dat")
265         except:
266                 print("Couldn't find calibration file for ADC " + str(channel))
267                 return False
268         
269         calibrate["ADC"][channel] = []
270         for l in input_file:
271                 l = l.split("#")[0].strip("\r\n ")
272                 if (l == ""):
273                         continue
274                 else:
275                         split_line = l.split("\t")
276                         calibrate["ADC"][channel].append((float(split_line[0]), float(split_line[1])))
277         input_file.close()
278
279         if (len(calibrate["ADC"][channel]) <= 0):
280                 print("Empty calibration file for ADC " + str(channel))
281                 return False
282         return True
283
284 def loadCalibration_DAC():
285         try:
286                 input_file = open("calibrateDAC.dat")
287         except:
288                 print("Couldn't find calibration file for DAC")
289                 return False
290         
291         calibrate["DAC"] = []
292         for l in input_file:
293                 #print("Line is: "+str(l))
294                 l = l.split("#")[0].strip("\r\n ")
295                 if (l == ""):
296                         continue
297                 else:
298                         split_line = l.split("\t")
299                         if (len(split_line) >= 3):
300                                 calibrate["DAC"].append((int(split_line[0]), float(split_line[1]), float(split_line[2])))
301                         else:
302                                 calibrate["DAC"].append((int(split_line[0]), float(split_line[1])))
303
304
305         input_file.close()
306
307         if (len(calibrate["DAC"]) <= 0):
308                 print("Empty calibration file for DAC")
309                 return False
310         return True
311
312 def getADC_Voltage(channel, counts):
313         if (calibrate["ADC"][channel] == None or len(calibrate["ADC"][channel]) <= 0):
314                 if (calibrate["ADC_Rin"][channel] != None and calibrate["ADC_Rvar"][channel] != None):
315                         print("Warning: Using rough calibration for ADC"+str(channel) + " = " + str(counts))
316                         ratio = float(calibrate["ADC_Rin"][channel]) / (float(calibrate["ADC_Rin"][channel]) + float(calibrate["ADC_Rvar"][channel]))
317                         return ratio * (float(counts) / float(calibrate["ADC_Counts"][channel]) * Vref)
318                 else:
319                         print("Error: No calibration for ADC"+str(channel))
320                         return False
321
322         c = calibrate["ADC"][channel]
323         valueIndex = 1
324         for i in range(0, len(c)-1):
325                 if (c[i][0] <= counts and i + 1 < len(c)):
326                         grad = (float(c[i+1][valueIndex]) - float(c[i][valueIndex])) / (float(c[i+1][0]) - float(c[i][0]))
327                         value = float(c[i][valueIndex]) + grad * (float(counts) - float(c[i][0]))
328                         return value
329
330         
331         print("Warning: Extrapolating outside calibration range for DAC = " + str(counts))
332
333         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]))
334         value = float(c[len(c)-1][valueIndex]) + grad * (float(counts) - float(c[len(c)-1][0]))
335                 
336 def readADC(channel):
337         #for i in range(0, aquire["ADC_Averages"]):
338         #       ser.write("r "+str(channel)+"\r") # Send command to datalogger
339                 #time.sleep(aquire["response_wait"])
340         #       response = ser.readline().strip("#\r\n ")
341         #       if (response != "r "+str(channel)):
342         #               print("Received wierd response reading ADC ("+response+")")
343         #               return False                    
344                 #time.sleep(aquire["response_wait"])
345         #       values.append(int(ser.readline().strip("\r\n ")))
346         #       adc_sum += float(values[len(values)-1])
347         ser.write("r "+str(channel)+"\r")
348         response = ser.readline().strip("#\r\n")
349         if (response != "r "+str(channel)):
350                 print("Received wierd response reading ADC ("+response+")")
351                 return False                    
352         return ser.readline().strip("\r\n").split(" ")
353
354 def getDAC_Voltage(counts, gain = True):
355         if (calibrate["DAC"] == None or len(calibrate["DAC"]) <= 0):
356                 if (calibrate["DAC_Gain"] != None):
357                         print("Warning: Using rough calibration for DAC = " + str(counts))
358                         return float(counts) / float(calibrate["DAC_Counts"]) * float(calibrate["Vref"]) * float(calibrate["DAC_Gain"])
359                 else:
360                         print("Error: No calibrate for DAC")
361                         return False
362
363         valueIndex = 1
364         if (gain == False):
365                 valueIndex = 2
366                 if (len(calibrate["DAC"][0]) < 3):
367                         print("Error: No data for unamplified DAC")
368                         return False
369         
370         c = calibrate["DAC"]
371         if (len(c) == 1):
372                 print("Warning: Only one point in calibration data!")
373                 return float(c[0][valueIndex])
374         
375         for i in range(0, len(c)-1):
376                 if (c[i][0] <= counts and i + 1 < len(c)):
377                         grad = (float(c[i+1][valueIndex]) - float(c[i][valueIndex])) / (float(c[i+1][0]) - float(c[i][0]))
378                         value = float(c[i][valueIndex]) + grad * (float(counts) - float(c[i][0]))
379                         return value
380
381         
382         print("Warning: Extrapolating outside calibration range for DAC = " + str(counts))
383
384         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]))
385         value = float(c[len(c)-1][valueIndex]) + grad * (float(counts) - float(c[len(c)-1][0]))
386         return value
387
388
389 def setDAC(level):
390         
391         ser.write("d "+str(level)+"\r") 
392         
393         response = ser.readline().strip("#\r\n ")
394         if (response != "d "+str(level)):
395                 print("Recieved wierd response setting DAC to "+str(level) + " ("+response+")")
396                 return False
397         #time.sleep(aquire["response_wait"])
398         #time.sleep(aquire["DAC_Settle"])
399         #time.sleep(aquire["DAC_Settle"])
400         response = ser.readline().strip("#\r\n ")
401         if (response != "DAC "+str(level)):
402                 print("Recieved wierd response setting DAC to "+str(level) + " ("+response+")")
403                 return False
404         #time.sleep(aquire["response_wait"])
405         time.sleep(aquire["DAC_Settle"])
406                 
407
408 def calibrateADC_usingDAC(channel, gain = True):
409         if (calibrate["DAC"] == None):
410                 print("ERROR: DAC is not calibrated, can't use to calibrate ADC!")
411                 return False
412
413         calibrate["ADC"][channel] = []
414         outfile = open("calibrateADC"+str(channel)+".dat", "w", 1)
415         outfile.write("# Calibrate ADC " + str(channel) + "\n")
416         outfile.write("# Start " + str(datetime.datetime.now()) + "\n")
417
418         print("Connect DAC output to ADC " + str(channel) + " and press enter\n")
419         sys.stdin.readline()
420         
421         for dac in calibrate["DAC"]:
422                 if (setDAC(dac[0]) == False):
423                         return False
424                 
425                 value = readADC(channel)
426                 if (value == False):
427                         return False
428                 canadd = (len(calibrate["ADC"][channel]) <= 0)
429                 if (canadd == False):
430                         canadd = True
431                         for adc in calibrate["ADC"][channel]:
432                                 if adc[0] == value[0]:
433                                         canadd = False
434                                         break
435
436                 if (canadd):            
437
438                         if gain:
439                                 input_value = dac[1]
440                         else:
441                                 input_value = dac[2]
442
443                         #input_value = getDAC_Voltage(dac[0], gain)
444                         if (input_value == False):
445                                 return False
446         
447                         outfile.write(str(value[0]) + "\t" + str(input_value) + "\t" + str(value[1]) + "\n")
448                         print(str(value[0]) + "\t" + str(input_value) + "\t" + str(value[1]))
449
450                         calibrate["ADC"][channel].append((value[0], input_value, value[1]))
451
452         outfile.write("# Stop " + str(datetime.datetime.now()) + "\n# EOF\n")
453         outfile.close()
454         if (setDAC(0) == False):
455                 return False
456         if (len(calibrate["ADC"][channel]) <= 0):
457                 print("Error: No calibration points taken for ADC " + str(channel))
458                 return False
459         return True
460
461 def calibrateADC(channel):      
462         calibrate["ADC"][channel] = []
463         outfile = open("calibrateADC"+str(channel)+".dat", "w", 1)
464         outfile.write("# Calibrate ADC " + str(channel) + "\n")
465         outfile.write("# Start " + str(datetime.datetime.now()) + "\n")
466
467         print("Calibrating ADC...\n")
468         print("Enter measured voltage, empty line stops.\n")
469
470         while True:
471                 read = sys.stdin.readline().strip("\r\n ")
472                 if (read == ""):
473                         break
474                 input_value = float(read)
475                 output_value = readADC(channel)
476                 if (output_value == False):
477                         return False
478                 
479                 calibrate["ADC"][channel].append((output_value[0], input_value))
480                 print(str(output_value[0]) + "\t" + str(input_value))
481                 
482         calibrate["ADC"][channel].sort(lambda e : e[1], reverse = False)
483         
484         
485         
486
487         outfile.write("# Stop " + str(datetime.datetime.now()) + "\n# EOF\n")
488         outfile.close()
489         if (len(calibrate["ADC"][channel]) <= 0):
490                 print("Error: No calibration points taken for ADC " + str(channel))
491                 return False
492
493         return True
494
495 def calibrateDAC():
496         calibrate["DAC"] = []
497
498         outfile = open("calibrateDAC.dat", "w", 1)
499         outfile.write("# Calibrate DAC\n")
500         outfile.write("# Start " + str(datetime.datetime.now()) + "\n")
501
502         print("Calibrating DAC...")
503         sys.stdout.write("Number of counts to increment per step: ")
504         read = sys.stdin.readline().strip("\r\n")
505         if (read == ""):
506                 return False
507         stepSize = max(1, int(read))
508         sys.stdout.write("\n")
509
510         outfile.write("# Increment by " + str(stepSize) + "\n")
511         print("Input measured DAC voltage for each level; empty input ends calibration.")
512         print("You may optionally input the DAC voltage before it is amplified too.")
513
514         level = 0
515         while (level < calibrate["DAC_Counts"]):
516
517                 setDAC(level)
518
519                 sys.stdout.write(str(level) + " ?")
520                 read = sys.stdin.readline().strip("\r\n ")
521                 if (read == ""):
522                         break
523                 else:
524                         read = read.split(" ")
525                         if (len(calibrate["DAC"]) > 0 and len(calibrate["DAC"][len(calibrate["DAC"])-1]) != len(read)):
526                                 print("Either give one value for EVERY point, or two values for EVERY point. Don't mess around.")
527                                 return False
528                         if (len(read) == 2):
529                                 calibrate["DAC"].append((level, float(read[0]), float(read[1])))
530                                 outfile.write(str(level) + "\t" + str(float(read[0])) + "\t" + str(float(read[1])) + "\n")
531                         else:
532                                 calibrate["DAC"].append((level, float(read[0])))
533                                 outfile.write(str(level) + "\t" + str(float(read[0])) + "\n")
534                 
535
536
537                 level += stepSize
538
539         outfile.write("# Stop " + str(datetime.datetime.now()) + "\n# EOF\n")
540         outfile.close()
541         if (len(calibrate["DAC"]) <= 0):
542                 print("Error: No calibration points taken for DAC")
543                 return False
544         return setDAC(0)
545
546 # Run the main function
547 if __name__ == "__main__":
548         sys.exit(main())

UCC git Repository :: git.ucc.asn.au