Automatic commit. Wed Aug 1 15:00:03 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/200)", # 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         "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
72 }
73
74 def getTime():
75         return str(datetime.datetime.now()).split(" ")[1].split(".")[0].replace(":","")
76
77 def getDate():
78         return str(datetime.datetime.now()).split(" ")[0]
79
80 def init():
81
82         
83         aquire["start_date"] = getDate()
84
85
86
87         # Connect serial
88         ser.open()
89         ser.isOpen()
90 #       print("Waiting for \"# hello\" from device...")
91 #       while (ser.readline().strip("\r\n") != "# hello"):
92 #               pass
93         #while (ser.readline().strip("\r\n ") != "#"):
94         #       pass
95         time.sleep(1.0)
96
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"))
101
102         print("Writing config information to config.dat...")
103         output = open("config.dat", "w", 1)
104
105         output.write("# Initialise " + str(datetime.datetime.now()) + "\n")
106
107         for field in calibrate:
108                 output.write("# calibrate["+str(field)+"] = "+str(calibrate[field]) + "\n")
109         output.write("\n")
110         for field in aquire:
111                 output.write("# aquire["+str(field)+"] = "+str(aquire[field]) + "\n")
112
113         output.write("# Ready " + str(datetime.datetime.now()) + "\n# EOF\n")
114         output.close()
115
116 def main():
117
118         init()
119         
120         if (loadCalibration_DAC() == False):
121                 if (calibrateDAC() == False):
122                         return -1
123         if (loadCalibration_ADC(aquire["ADC_Is"]) == False):
124                 if (calibrateADC_usingDAC(aquire["ADC_Is"], False) == False):
125                         if (calibrateADC(aquire["ADC_Is"]) == False):
126                                 return -1
127
128         if (loadCalibration_ADC(aquire["ADC_Vi"]) == False):
129                 if (calibrateADC_usingDAC(aquire["ADC_Vi"], True) == False):
130                         if (calibrateADC(aquire["ADC_Vi"]) == False):
131                                 return -1
132         
133
134         # Make directory for today, backup calibration files
135         os.system("mkdir -p " + getDate())
136         os.system("cp *.dat " + getDate() +"/")
137
138         #checkList()
139
140         
141         comment = None
142                 
143
144         # Experiment
145         # TODO: Modify data to record here
146         sweep = 1
147         #record_data([4, 5], getDate()+"/"+str(getTime())+".dat", None, None, "Measure emission&sample current varying with time, constant initial energy.")
148         while True:
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"]+")")
151                 sweep += 1
152         
153
154 def checkList():
155         
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")
162
163
164 def record_data(ADC_channels, output, pollTime = None, dac_max = None, comment = None):
165         if (output != 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")
171         else:
172                 output = [sys.stdout]
173
174         start_time = time.time()
175         
176         for out in output:
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))
184                 out.write("\n")
185
186         step = 0
187         dacValue = int(eval(aquire["DAC_Sweep"]))
188         if (setDAC(dacValue) == False):
189                 setDAC(dacValue)
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
195                                 if (dacValue < 0):
196                                         break
197                                 setDAC(dacValue)
198                         step += 1
199                 
200
201                 if (dac_max != None and dacValue >= dac_max):
202                         break
203
204                 measure_start = time.time()
205
206                 raw_adc = []
207
208                 for channel in ADC_channels:
209                         read = readADC(channel)
210                         if read == False:
211                                 print("Abort data collection")
212                                 return False
213                         raw_adc.append((channel, read[0], read[1]))
214
215                 end_time = time.time()
216
217                 for out in output:
218                         out.write(str((measure_start + (end_time - measure_start)/2.0 - start_time)))
219                         out.write("\t"+str(dacValue))
220                         for adc in raw_adc:
221                                 out.write("\t" + str(adc[1]) + "\t" + str(adc[2]))
222                         out.write("\n") 
223         
224                 
225         for out in output:              
226                 if out != sys.stdout:
227                         out.close()
228         return True
229
230 def loadCalibration_ADC(channel):
231         try:
232                 input_file = open("calibrateADC"+str(channel)+".dat")
233         except:
234                 print("Couldn't find calibration file for ADC " + str(channel))
235                 return False
236         
237         calibrate["ADC"][channel] = []
238         for l in input_file:
239                 l = l.split("#")[0].strip("\r\n ")
240                 if (l == ""):
241                         continue
242                 else:
243                         split_line = l.split("\t")
244                         calibrate["ADC"][channel].append((float(split_line[0]), float(split_line[1])))
245         input_file.close()
246
247         if (len(calibrate["ADC"][channel]) <= 0):
248                 print("Empty calibration file for ADC " + str(channel))
249                 return False
250         return True
251
252 def loadCalibration_DAC():
253         try:
254                 input_file = open("calibrateDAC.dat")
255         except:
256                 print("Couldn't find calibration file for DAC")
257                 return False
258         
259         calibrate["DAC"] = []
260         for l in input_file:
261                 #print("Line is: "+str(l))
262                 l = l.split("#")[0].strip("\r\n ")
263                 if (l == ""):
264                         continue
265                 else:
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])))
269                         else:
270                                 calibrate["DAC"].append((int(split_line[0]), float(split_line[1])))
271
272
273         input_file.close()
274
275         if (len(calibrate["DAC"]) <= 0):
276                 print("Empty calibration file for DAC")
277                 return False
278         return True
279
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)
286                 else:
287                         print("Error: No calibration for ADC"+str(channel))
288                         return False
289
290         c = calibrate["ADC"][channel]
291         valueIndex = 1
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]))
296                         return value
297
298         
299         print("Warning: Extrapolating outside calibration range for DAC = " + str(counts))
300
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]))
303                 
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+")")
311         #               return False                    
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+")")
319                 return False                    
320         return ser.readline().strip("\r\n").split(" ")
321
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"])
327                 else:
328                         print("Error: No calibrate for DAC")
329                         return False
330
331         valueIndex = 1
332         if (gain == False):
333                 valueIndex = 2
334                 if (len(calibrate["DAC"][0]) < 3):
335                         print("Error: No data for unamplified DAC")
336                         return False
337         
338         c = calibrate["DAC"]
339         if (len(c) == 1):
340                 print("Warning: Only one point in calibration data!")
341                 return float(c[0][valueIndex])
342         
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]))
347                         return value
348
349         
350         print("Warning: Extrapolating outside calibration range for DAC = " + str(counts))
351
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]))
354         return value
355
356
357 def setDAC(level):
358         
359         ser.write("d "+str(level)+"\r") 
360         
361         response = ser.readline().strip("#\r\n ")
362         if (response != "d "+str(level)):
363                 print("Recieved wierd response setting DAC to "+str(level) + " ("+response+")")
364                 return False
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+")")
371                 return False
372         #time.sleep(aquire["response_wait"])
373         time.sleep(aquire["DAC_Settle"])
374                 
375
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!")
379                 return False
380
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")
385
386         print("Connect DAC output to ADC " + str(channel) + " and press enter\n")
387         sys.stdin.readline()
388         
389         for dac in calibrate["DAC"]:
390                 if (setDAC(dac[0]) == False):
391                         return False
392                 
393                 value = readADC(channel)
394                 if (value == False):
395                         return False
396                 canadd = (len(calibrate["ADC"][channel]) <= 0)
397                 if (canadd == False):
398                         canadd = True
399                         for adc in calibrate["ADC"][channel]:
400                                 if adc[0] == value[0]:
401                                         canadd = False
402                                         break
403
404                 if (canadd):            
405
406                         if gain:
407                                 input_value = dac[1]
408                         else:
409                                 input_value = dac[2]
410
411                         #input_value = getDAC_Voltage(dac[0], gain)
412                         if (input_value == False):
413                                 return False
414         
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]))
417
418                         calibrate["ADC"][channel].append((value[0], input_value, value[1]))
419
420         outfile.write("# Stop " + str(datetime.datetime.now()) + "\n# EOF\n")
421         outfile.close()
422         if (setDAC(0) == False):
423                 return False
424         if (len(calibrate["ADC"][channel]) <= 0):
425                 print("Error: No calibration points taken for ADC " + str(channel))
426                 return False
427         return True
428
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")
434
435         print("Calibrating ADC...\n")
436         print("Enter measured voltage, empty line stops.\n")
437
438         while True:
439                 read = sys.stdin.readline().strip("\r\n ")
440                 if (read == ""):
441                         break
442                 input_value = float(read)
443                 output_value = readADC(channel)
444                 if (output_value == False):
445                         return False
446                 
447                 calibrate["ADC"][channel].append((output_value[0], input_value))
448                 print(str(output_value[0]) + "\t" + str(input_value))
449                 
450         calibrate["ADC"][channel].sort(lambda e : e[1], reverse = False)
451         
452         
453         
454
455         outfile.write("# Stop " + str(datetime.datetime.now()) + "\n# EOF\n")
456         outfile.close()
457         if (len(calibrate["ADC"][channel]) <= 0):
458                 print("Error: No calibration points taken for ADC " + str(channel))
459                 return False
460
461         return True
462
463 def calibrateDAC():
464         calibrate["DAC"] = []
465
466         outfile = open("calibrateDAC.dat", "w", 1)
467         outfile.write("# Calibrate DAC\n")
468         outfile.write("# Start " + str(datetime.datetime.now()) + "\n")
469
470         print("Calibrating DAC...")
471         sys.stdout.write("Number of counts to increment per step: ")
472         read = sys.stdin.readline().strip("\r\n")
473         if (read == ""):
474                 return False
475         stepSize = max(1, int(read))
476         sys.stdout.write("\n")
477
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.")
481
482         level = 0
483         while (level < calibrate["DAC_Counts"]):
484
485                 setDAC(level)
486
487                 sys.stdout.write(str(level) + " ?")
488                 read = sys.stdin.readline().strip("\r\n ")
489                 if (read == ""):
490                         break
491                 else:
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.")
495                                 return False
496                         if (len(read) == 2):
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")
499                         else:
500                                 calibrate["DAC"].append((level, float(read[0])))
501                                 outfile.write(str(level) + "\t" + str(float(read[0])) + "\n")
502                 
503
504
505                 level += stepSize
506
507         outfile.write("# Stop " + str(datetime.datetime.now()) + "\n# EOF\n")
508         outfile.close()
509         if (len(calibrate["DAC"]) <= 0):
510                 print("Error: No calibration points taken for DAC")
511                 return False
512         return setDAC(0)
513
514 # Run the main function
515 if __name__ == "__main__":
516         sys.exit(main())

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