Automatic commit. Thu Aug 9 10:00:04 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 import collections
16
17
18 # TODO: Insert variables for calibration purposes here.
19 calibrate = {
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)
22
23         # Calibration data for DAC and ADC
24         "DAC" : None, 
25         "ADC" : [None, None, None, None, None, None, None, None],
26
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],
31         "DAC_Gain" : 1
32 }
33
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!)
36         "ADC_Averages" : 200,
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
42         "start_date" : None,
43         "open_files" : []
44 }
45
46 #Setup the serial connection parameters
47 ser = serial.Serial(
48         port="/dev/ttyUSB0", # Modify as needed (note: in linux need to run `sudo chmod a+rw /dev/ttyUSBX' to set permissions)
49
50         # Do not change the values below here (unless AVR butterfly is reprogrammed to use different values)
51         baudrate=4800,
52         parity="N",
53         stopbits=1,
54         bytesize=8,
55         timeout=None,
56         xonxoff=0,
57         rtscts=0
58 )
59
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),
69         ("610B Zero" , None),
70         ("602 Zero" , None),
71         ("610B Scale" , None),
72         ("602 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),
80         ("Title" , None),
81         ("Comment" , None),
82         ("Data" , None),
83 ])
84
85 def getTime():
86         return str(datetime.datetime.now()).split(" ")[1].split(".")[0].replace(":","")
87
88 def getDate():
89         return str(datetime.datetime.now()).split(" ")[0]
90
91 # Used for when I press Control-C to stop things
92 def set_exit_handler(func):
93     if os.name == "nt":
94         try:
95             import win32api
96             win32api.SetConsoleCtrlHandler(func, True)
97         except ImportError:
98             version = “.”.join(map(str, sys.version_info[:2]))
99             raise Exception(”pywin32 not installed for Python ” + version)
100     else:
101         import signal
102         signal.signal(signal.SIGTERM, func)
103
104 def exit_handler():
105         
106
107 def init():
108
109         
110         aquire["start_date"] = getDate()
111
112
113
114         # Connect serial
115         ser.open()
116         ser.isOpen()
117 #       print("Waiting for \"# hello\" from device...")
118 #       while (ser.readline().strip("\r\n") != "# hello"):
119 #               pass
120         #while (ser.readline().strip("\r\n ") != "#"):
121         #       pass
122         time.sleep(1.0)
123
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"))
128
129         print("Writing config information to config.dat...")
130         output = open("config.dat", "w", 1)
131
132         output.write("# Initialise " + str(datetime.datetime.now()) + "\n")
133
134         #for field in calibrate:
135         #       output.write("# calibrate["+str(field)+"] = "+str(calibrate[field]) + "\n")
136         #output.write("\n")
137         for field in aquire:
138                 output.write("# aquire["+str(field)+"] = "+str(aquire[field]) + "\n")
139
140         output.write("# Ready " + str(datetime.datetime.now()) + "\n# EOF\n")
141         output.close()
142
143 def main():
144
145         init()
146         
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):
150         #               return -1
151         #if (loadCalibration_ADC(aquire["ADC_Is"]) == False):
152         #       if (calibrateADC_usingDAC(aquire["ADC_Is"], False) == False):
153         #               if (calibrateADC(aquire["ADC_Is"]) == False):
154         #                       return -1
155
156         #if (loadCalibration_ADC(aquire["ADC_Vi"]) == False):
157         #       if (calibrateADC_usingDAC(aquire["ADC_Vi"], True) == False):
158         #               if (calibrateADC(aquire["ADC_Vi"]) == False):
159         #                       return -1
160         
161
162         # Make directory for today, backup calibration files
163         os.system("mkdir -p " + getDate())
164         #os.system("cp *.dat " + getDate() +"/")
165
166         checkList()
167
168         
169         
170                 
171
172         # Experiment
173         # TODO: Modify data to record here
174         sweep = 1
175         while True:
176                 os.system("mkdir -p " + getDate())
177                 record_data([4, 5], getDate()+"/"+str(getTime())+".dat", None, 4000)
178                 sweep += 1
179         
180
181 def checkList(output_file):
182         try:
183                 input_file = open(getDate()+"/checklist", "r")
184         except:
185                 input_file = None
186
187         if (input_file != None):
188                 for line in input_file:
189                         k = line.split("=")
190                         item = k[0].strip(" \r\n")
191                         value = k[1].strip(" \r\n")
192                         if (item in parameters):
193                                 parameters[item] = value
194         
195                 print("Checklist found. Overwrite? [Y/n]")
196                 response = sys.stdin.readline().strip(" \r\n")
197                 if (response == "" or response == "y" or response == "Y"):
198                         input_file = None
199         
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 ")
204                         if (response != ""):
205                                 parameters[item] = response
206                         sys.stdout.write("\n")
207                         
208
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")
213
214
215
216 def record_data(ADC_channels, output, pollTime = None, dac_max = None):
217         
218         if (output != None):
219                 output = [open(output, "w", 0), sys.stdout]
220                 checkList(output[0])
221         else:
222                 output = [sys.stdout]
223
224         for field in aquire:
225                 for out in output:
226                         out.write("# aquire["+str(field)+"] = "+str(aquire[field]) + "\n")
227         
228         for out in output:
229                 out.write("# Checklist:\n")
230
231         for field in checklist:
232                 for out in output:
233                         out.write("# "+str(field)+" = " + str(checklist[field]) + "\n")
234
235
236         start_time = time.time()
237         
238         for out in output:
239                 out.write("\n")
240                 out.write("# Experiment " + str(datetime.datetime.now()) + "\n")
241                 out.write("# Polling for " + str(pollTime) + "s.\n")
242                 out.write("\n")
243                 out.write("# Data:\n")
244                 out.write("# time\tDAC")
245                 for channel in ADC_channels:
246                         out.write("\tADC"+str(channel))
247                 out.write("\n")
248
249         step = 0
250         dacValue = int(eval(aquire["DAC_Sweep"]))
251         if (setDAC(dacValue) == False):
252                 setDAC(dacValue)
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
258                                 if (dacValue < 0):
259                                         break
260                                 setDAC(dacValue)
261                         step += 1
262                 
263
264                 if (dac_max != None and dacValue >= dac_max):
265                         break
266
267                 measure_start = time.time()
268
269                 raw_adc = []
270
271                 for channel in ADC_channels:
272                         read = readADC(channel)
273                         if read == False:
274                                 print("Abort data collection")
275                                 return False
276                         raw_adc.append((channel, read[0], read[1]))
277
278                 end_time = time.time()
279
280                 for out in output:
281                         out.write(str((measure_start + (end_time - measure_start)/2.0 - start_time)))
282                         out.write("\t"+str(dacValue))
283                         for adc in raw_adc:
284                                 out.write("\t" + str(adc[1]) + "\t" + str(adc[2]))
285                         out.write("\n") 
286         
287                 
288         for out in output:              
289                 if out != sys.stdout:
290                         out.close()
291         return True
292
293 def loadCalibration_ADC(channel):
294         try:
295                 input_file = open("calibrateADC"+str(channel)+".dat")
296         except:
297                 print("Couldn't find calibration file for ADC " + str(channel))
298                 return False
299         
300         calibrate["ADC"][channel] = []
301         for l in input_file:
302                 l = l.split("#")[0].strip("\r\n ")
303                 if (l == ""):
304                         continue
305                 else:
306                         split_line = l.split("\t")
307                         calibrate["ADC"][channel].append((float(split_line[0]), float(split_line[1])))
308         input_file.close()
309
310         if (len(calibrate["ADC"][channel]) <= 0):
311                 print("Empty calibration file for ADC " + str(channel))
312                 return False
313         return True
314
315 def loadCalibration_DAC():
316         try:
317                 input_file = open("calibrateDAC.dat")
318         except:
319                 print("Couldn't find calibration file for DAC")
320                 return False
321         
322         calibrate["DAC"] = []
323         for l in input_file:
324                 #print("Line is: "+str(l))
325                 l = l.split("#")[0].strip("\r\n ")
326                 if (l == ""):
327                         continue
328                 else:
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])))
332                         else:
333                                 calibrate["DAC"].append((int(split_line[0]), float(split_line[1])))
334
335
336         input_file.close()
337
338         if (len(calibrate["DAC"]) <= 0):
339                 print("Empty calibration file for DAC")
340                 return False
341         return True
342
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)
349                 else:
350                         print("Error: No calibration for ADC"+str(channel))
351                         return False
352
353         c = calibrate["ADC"][channel]
354         valueIndex = 1
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]))
359                         return value
360
361         
362         print("Warning: Extrapolating outside calibration range for DAC = " + str(counts))
363
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]))
366                 
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+")")
374         #               return False                    
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+")")
382                 return False                    
383         return ser.readline().strip("\r\n").split(" ")
384
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"])
390                 else:
391                         print("Error: No calibrate for DAC")
392                         return False
393
394         valueIndex = 1
395         if (gain == False):
396                 valueIndex = 2
397                 if (len(calibrate["DAC"][0]) < 3):
398                         print("Error: No data for unamplified DAC")
399                         return False
400         
401         c = calibrate["DAC"]
402         if (len(c) == 1):
403                 print("Warning: Only one point in calibration data!")
404                 return float(c[0][valueIndex])
405         
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]))
410                         return value
411
412         
413         print("Warning: Extrapolating outside calibration range for DAC = " + str(counts))
414
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]))
417         return value
418
419
420 def setDAC(level):
421         
422         ser.write("d "+str(level)+"\r") 
423         
424         response = ser.readline().strip("#\r\n ")
425         if (response != "d "+str(level)):
426                 print("Recieved wierd response setting DAC to "+str(level) + " ("+response+")")
427                 return False
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+")")
434                 return False
435         #time.sleep(aquire["response_wait"])
436         time.sleep(aquire["DAC_Settle"])
437                 
438
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!")
442                 return False
443
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")
448
449         print("Connect DAC output to ADC " + str(channel) + " and press enter\n")
450         sys.stdin.readline()
451         
452         for dac in calibrate["DAC"]:
453                 if (setDAC(dac[0]) == False):
454                         return False
455                 
456                 value = readADC(channel)
457                 if (value == False):
458                         return False
459                 canadd = (len(calibrate["ADC"][channel]) <= 0)
460                 if (canadd == False):
461                         canadd = True
462                         for adc in calibrate["ADC"][channel]:
463                                 if adc[0] == value[0]:
464                                         canadd = False
465                                         break
466
467                 if (canadd):            
468
469                         if gain:
470                                 input_value = dac[1]
471                         else:
472                                 input_value = dac[2]
473
474                         #input_value = getDAC_Voltage(dac[0], gain)
475                         if (input_value == False):
476                                 return False
477         
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]))
480
481                         calibrate["ADC"][channel].append((value[0], input_value, value[1]))
482
483         outfile.write("# Stop " + str(datetime.datetime.now()) + "\n# EOF\n")
484         outfile.close()
485         if (setDAC(0) == False):
486                 return False
487         if (len(calibrate["ADC"][channel]) <= 0):
488                 print("Error: No calibration points taken for ADC " + str(channel))
489                 return False
490         return True
491
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")
497
498         print("Calibrating ADC...\n")
499         print("Enter measured voltage, empty line stops.\n")
500
501         while True:
502                 read = sys.stdin.readline().strip("\r\n ")
503                 if (read == ""):
504                         break
505                 input_value = float(read)
506                 output_value = readADC(channel)
507                 if (output_value == False):
508                         return False
509                 
510                 calibrate["ADC"][channel].append((output_value[0], input_value))
511                 print(str(output_value[0]) + "\t" + str(input_value))
512                 
513         calibrate["ADC"][channel].sort(lambda e : e[1], reverse = False)
514         
515         
516         
517
518         outfile.write("# Stop " + str(datetime.datetime.now()) + "\n# EOF\n")
519         outfile.close()
520         if (len(calibrate["ADC"][channel]) <= 0):
521                 print("Error: No calibration points taken for ADC " + str(channel))
522                 return False
523
524         return True
525
526 def calibrateDAC():
527         calibrate["DAC"] = []
528
529         outfile = open("calibrateDAC.dat", "w", 1)
530         outfile.write("# Calibrate DAC\n")
531         outfile.write("# Start " + str(datetime.datetime.now()) + "\n")
532
533         print("Calibrating DAC...")
534         sys.stdout.write("Number of counts to increment per step: ")
535         read = sys.stdin.readline().strip("\r\n")
536         if (read == ""):
537                 return False
538         stepSize = max(1, int(read))
539         sys.stdout.write("\n")
540
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.")
544
545         level = 0
546         while (level < calibrate["DAC_Counts"]):
547
548                 setDAC(level)
549
550                 sys.stdout.write(str(level) + " ?")
551                 read = sys.stdin.readline().strip("\r\n ")
552                 if (read == ""):
553                         break
554                 else:
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.")
558                                 return False
559                         if (len(read) == 2):
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")
562                         else:
563                                 calibrate["DAC"].append((level, float(read[0])))
564                                 outfile.write(str(level) + "\t" + str(float(read[0])) + "\n")
565                 
566
567
568                 level += stepSize
569
570         outfile.write("# Stop " + str(datetime.datetime.now()) + "\n# EOF\n")
571         outfile.close()
572         if (len(calibrate["DAC"]) <= 0):
573                 print("Error: No calibration points taken for DAC")
574                 return False
575         return setDAC(0)
576
577 # Run the main function
578 if __name__ == "__main__":
579         sys.exit(main())

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