import time from struct import * import ctypes import array import numpy as np import matplotlib.pyplot as plt import os import eel # GUI from ADUCv2p1 import * ################################### ## PYTHON UTILITIES ################################### last_wf_number = -1; last_timestamp = 0 def save_waveform(): chip = chips[state["selected_board"]] data = chip.read_data() gnd = sum(data[state["start_gnd"]:state["stop_gnd"]])/(state["stop_gnd"]-state["start_gnd"]) signal = sum(data[state["start"]:state["stop"]])/(state["stop"]-state["start"]) gnd_std = np.std(data[state["start_gnd"]:state["stop_gnd"]]) signal_std = np.std(data[state["start"]:state["stop"]]) wf_number = chip.get_wf_cnt() if(last_wf_number>0): state["board_freq"] = (timestamp - last_timestamp)/(wf_number - last_wf_number) last_wf_number, last_timestamp = wf_number, timestamp timestamp = time.time() tosave = [wf_number, timestamp, state["start"], state["stop"], state["start_gnd"], state["stop_gnd"], state["input_gain"], state["offset"], signal, gnd, signal_std, gnd_std, state["wf_len"]] + list(data) np.savetxt("data/waveforms/"+str(int(timestamp*100))+".csv", [tosave], delimiter=",", fmt='%10.5f') # Save more data for long term graphs tosave = [wf_number, timestamp, signal, gnd, signal-gnd, signal_std, gnd_std, chip.get_mode(), chip.get_out_of_lock(), chip.getVout(1), chip.getVout(2)] line = '\n'+','.join(map(str, tosave)) with open('data/long_term.csv','a') as fd: fd.write(line) update_state() import datetime def clean_old_files(path, max_Files): def sorted_ls(path): mtime = lambda f: os.stat(os.path.join(path, f)).st_mtime return list(sorted(os.listdir(path), key=mtime)) del_list = sorted_ls(path)[0:(len(sorted_ls(path))-max_Files)] for dfile in del_list: os.remove(path + dfile) '''for dirpath, dirnames, filenames in os.walk("data/waveforms/"): for file in filenames: curpath = os.path.join(dirpath, file) file_modified = datetime.datetime.fromtimestamp(os.path.getmtime(curpath)) if datetime.datetime.now() - file_modified > datetime.timedelta(hours=10): os.remove(curpath)''' @eel.expose def calibrate_gain(): if(state["mode"]!=0): error("You must be in learn mode to calibrate the loop gain") return chip = chips[state["selected_board"]] auto_set_pga = state["auto_set_pga"] chip.set_auto_set_pga(0) #Stop it while we scan measurements = [] for Vfine in range(2000, 4000, 100): chip.set_Vout(2, Vfine) time.sleep(0.1) data = chip.read_data() gnd = sum(data[state["start_gnd"]:state["stop_gnd"]])/(state["stop_gnd"]-state["start_gnd"]) signal = sum(data[state["start"]:state["stop"]])/(state["stop"]-state["start"]) print('Vfine', chip.get_Vout(2), 'signal', signal-gnd) measurements.append([chip.get_Vout(2), signal-gnd]) chip.set_Vout(2, 3000) measurements = np.array(measurements) p = np.polyfit(measurements[:,0], measurements[:,1], deg=1) print('signal-gnd vs Vfine slope', p[0]) chip.set_Gain(1/p[0]) print('Gain has been set to 1/slope', 1/p[0]) plt.plot(np.arange(2000, 4000, 100), measurements) plt.plot(np.range(2000, 4000, 100), np.range(2000, 4000, 100)*p[0] + p[1]) plt.show() chip.set_auto_set_pga(auto_set_pga) update_state() @eel.expose def calibrate_coarse_fine_ratio(): if(state["mode"]!=0): error("You must be in learn mode to calibrate the broad to fine ratio") return chip = chips[state["selected_board"]] auto_set_pga = state["auto_set_pga"] chip.set_auto_set_pga(0) #Stop it while we scan # Scan Vfine measurements = [] for Vfine in range(2500, 3500, 100): chip.set_Vout(2, Vfine) time.sleep(0.1) data = chip.read_data() gnd = sum(data[state["start_gnd"]:state["stop_gnd"]])/(state["stop_gnd"]-state["start_gnd"]) signal = sum(data[state["start"]:state["stop"]])/(state["stop"]-state["start"]) print('Vfine', chip.get_Vout(2), 'signal', signal-gnd) measurements.append([chip.get_Vout(2), signal-gnd]) chip.set_Vout(2, 3000) measurements = np.array(measurements) p = np.polyfit(measurements[:,0], measurements[:,1], deg=1) # Scan Vcoarse measurements = [] low, high = state["Vlearn"]-100, state["Vlearn"]+100 if(low<0): low=0 if(high>4000): high=4000 for Vcoarse in range(low, high, 20): chip.set_Vlearn(Vcoarse) time.sleep(0.1) data = chip.read_data() gnd = sum(data[state["start_gnd"]:state["stop_gnd"]])/(state["stop_gnd"]-state["start_gnd"]) signal = sum(data[state["start"]:state["stop"]])/(state["stop"]-state["start"]) print('Vcoarse', chip.get_Vlearn(), 'signal', signal-gnd) measurements.append([chip.get_Vlearn(), signal-gnd]) chip.set_Vlearn(state["Vlearn"]) measurements = np.array(measurements) q = np.polyfit(measurements[:,0], measurements[:,1], deg=1) # Set to ratio chip.set_coarse_fine_ratio(q[0]/p[0]) chip.set_auto_set_pga(auto_set_pga) update_state() @eel.expose def measure_response_function(): if(state["mode"]!=0): error("You must be in learn mode to measure the response function.") return chip = chips[state["selected_board"]] auto_set_pga = state["auto_set_pga"] chip.set_auto_set_pga(0) #Stop it while we scan # Scan Vcoarse measurements = [] for Vcoarse in range(0, 4000, 100): chip.set_Vlearn(Vcoarse) time.sleep(0.1) data = chip.read_data() gnd = sum(data[state["start_gnd"]:state["stop_gnd"]])/(state["stop_gnd"]-state["start_gnd"]) signal = sum(data[state["start"]:state["stop"]])/(state["stop"]-state["start"]) print('Vcoarse', chip.get_Vlearn(), 'signal', signal-gnd) measurements.append([chip.get_Vlearn(), signal-gnd]) chip.set_Vlearn(state["Vlearn"]) measurements = np.array(measurements) plt.plot(measurements[:,0], measurements[:,1]) plt.xlabel('Output voltage') plt.ylabel('Photodiode signal') plt.plot() chip.set_auto_set_pga(auto_set_pga) update_state() @eel.expose def sdev_time(): pass @eel.expose def noise_power_spectrum(): pass ################################### ## EXPOSED GUI COMMUNICATION ################################### # Graphs from os import listdir @eel.expose def listFiles(path): return listdir(path) @eel.expose def loadFile(path): return list(np.loadtxt(path, delimiter=",")) @eel.expose def loadLongTerm(): li = list(np.loadtxt('data/long_term.csv', delimiter=",")) if((li[-1][1]-li[0][1])/60/60 > 24 or len(li)>100000): # Time to clean up, delete until we have less than 90000 points and 20 hours of data with open('data/long_term.csv') as f, open("data/long_term_tmp.csv", "w") as out: for x in range(len(li)): if(len(li)-x>90000 or (li[-1][1]-li[x][1])/60/60 > 20): next(f) for line in f: out.write(line) os.remove("data/long_term.csv") os.rename("data/long_term_tmp.csv", "data/long_term.csv") return [list(l) for l in li] # Expose functions to GUI and do some parameter checking def error(txt): eel.error(txt) def warning(txt): eel.warning(txt) @eel.expose def set_selected_board(n): if n>len(chips): error("Selected board does not exist.") return; state["selected_board"] = n update_state() @eel.expose def set_pi_freq(freq): if(freq>100): error("The communication cannot be this fast. High values are likely to disturb the board.") return if(freq<0.2): error("Frequency must be at least 0.2Hz.") return if(freq>10): warning("High values are likely to disturb the board.") state["pi_freq"] = freq eel.renderUI(state) @eel.expose def set_remote_trigg(status): status = int(status) if status not in [0, 1]: error("remote_trigg must be set to either 0 or 1.") return return chips[state["selected_board"]].set_remote_trigg(status) update_state() @eel.expose def set_enab_gnd(status): status = int(status) if status not in [0, 1]: error("enab_gnd must be set to either 0 or 1.") return return chips[state["selected_board"]].set_enab_gnd(status) update_state() @eel.expose def set_Vlearn(Vlearn): if Vlearn>4095 or Vlearn<0: error("Vlearn must be in the range 0-4095") return if Vlearn<2000: warning("Low values of Vlearn will heavily attenuate the output. See the response curve.") if Vlearn>3500: warning("High values of Vlearn may not leave enough room for the stabilization process and may result in the board going out of loop. See the response curve.") return chips[state["selected_board"]].set_Vlearn(Vlearn) update_state() @eel.expose def set_start(start): if start>255 or start<0: error("start must be in the range 0-256") return if start>state["stop"]: error("start must be lower than stop") return if start>state["wf_len"]: error("start must be lower than the waveform length") return return chips[state["selected_board"]].set_start(start) update_state() @eel.expose def set_stop(stop): if stop>255 or stop<0: error("stop must be in the range 0-256") return if stopstate["wf_len"]: error("stop must be lower than the waveform length") return return chips[state["selected_board"]].set_stop(stop) update_state() @eel.expose def set_start_gnd(start_gnd): if start_gnd>255 or start_gnd<0: error("start_gnd must be in the range 0-256") return if start_gnd>state["stop_gnd"]: error("start_gnd must be lower than stop") return if start_gnd>state["wf_len"]: error("start_gnd must be lower than the waveform length") return return chips[state["selected_board"]].set_start_gnd(start_gnd) update_state() @eel.expose def set_stop_gnd(stop_gnd): if stop_gnd>255 or stop_gnd<0: error("stop_gnd must be in the range 0-256") return if stop_gndstate["wf_len"]: error("stop_gnd must be lower than the waveform length") return return chips[state["selected_board"]].set_stop_gnd(stop_gnd) update_state() @eel.expose def set_wf_len(*params): if wf_len>255 or wf_len<0: error("wf_len must be in the range 0-256") return if wf_len 5: warning("Note that increasing the Number of waveforms per stabilization loop decreases the speed of the stabilization. We typically just have N=1.") return chips[state["selected_board"]].set_N(N) update_state() @eel.expose def set_step_max(step): if step>4095 or step<1: error("step_max must be in the range 0-1") return if step<50: warning("step_max seems low. This may limit the ability to react to fast power fluctuations.") return chips[state["selected_board"]].set_step_max(step) update_state() @eel.expose def set_Gain(*params): return chips[state["selected_board"]].set_Gain(*params) update_state() @eel.expose def set_auto_set_pga(status): if status not in [0, 1]: error("auto_set_pga must be set to either 0 or 1.") return return chips[state["selected_board"]].set_auto_set_pga(status) update_state() @eel.expose def set_input_gain(input_gain): if input_gain not in [1, 2, 4, 8, 16, 32, 64, 128]: error("input_gain must be one of [1, 2, 4, 8, 16, 32, 64, 128]") return return chips[state["selected_board"]].set_input_gain(input_gain) update_state() @eel.expose def set_offset(offset): if offset>4095 or offset<0: error("offset must be in the range 0-4095. 2000 corresponds to no offset.") return return chips[state["selected_board"]].set_offset(offset) update_state() @eel.expose def set_coarse_fine_ratio(coarse_fine_ratio): if coarse_fine_ratio<10 or coarse_fine_ratio>30: error("coarse_fine_ratio should be approximately 20.") return if coarse_fine_ratio<15 or coarse_fine_ratio>25: warning("coarse_fine_ratio should be approximately 20.") return chips[state["selected_board"]].set_coarse_fine_ratio(coarse_fine_ratio) update_state() # Program state state = { "n_boards": 2, "selected_board": 0, "box_address": 0x50, "mode": 0, "out_of_lock": 0, "pi_freq": 1, "board_freq": 0, "remote_trigg": 0, "enab_gnd": 1, "Vlearn": 3200, "start": 25, "stop": 100, "start_gnd": 125, "stop_gnd": 200, "wf_len": 200, "N": 1, "step_max": 200, "Gain": 1.0, "auto_set_pga": 1, "input_gain": 1, "offset": 2000, "coarse_fine_ratio": 20 } def update_state(): print(state) state["n_boards"] = len(chips) state["box_address"] = adresses[state["selected_board"]] chip = chips[state["selected_board"]] state["enab_gnd"] = chip.get_enab_gnd() state["mode"] = chip.get_mode() state["out_of_lock"] = chip.get_out_of_lock() state["remote_trigg"] = chip.get_remote_trigg() state["Vlearn"] = chip.get_Vlearn() state["start"] = chip.get_start() state["stop"] = chip.get_stop() state["start_gnd"] = chip.get_start_gnd() state["stop_gnd"] = chip.get_stop_gnd() state["wf_len"] = chip.get_wf_len() state["N"] = chip.get_N() state["step_max"] = chip.get_step_max() state["Gain"] = chip.get_Gain() state["auto_set_pga"] = chip.get_auto_set_pga() state["input_gain"] = chip.get_input_gain() state["offset"] = chip.get_offset() state["coarse_fine_ratio"] = chip.get_coarse_fine_ratio() for l in state: if isinstance(state[l], np.uint16) or isinstance(state[l], np.int32): state[l] = int(state[l]) if isinstance(state[l], np.float32): state[l] = float(state[l]) print(type(state[l])) for l in state: print(type(state[l])) print(state) eel.renderUI(state) ################################### ## START THE GUI ################################### my_options = { 'mode': "chrome-app", #chrome-app 'host': 'localhost', 'port': 8000 + int(np.random.rand()*1000), 'size':(710, 725), #'chromeFlags': ["--start-fullscreen", "--browser-startup-dialog"] } eel.init('web') eel.start('main.html', options=my_options, block=False) # Detect all boards import os import subprocess import re chips = [] adresses = [] # TO DO p = subprocess.Popen(['i2cdetect', '-y','1'],stdout=subprocess.PIPE,) p.stdout.readline() for i in range(0,8): line = str(p.stdout.readline())[4:] for match in re.finditer("[0-9a-f]+", line): adresses.append(int(match.group(0), 16)) chips.append(ADUCv2p1(int(match.group(0), 16),True)) print('Found boards', adresses, chips) #update_state() eel.renderUI(state) eel.sleep(5) update_state() eel.renderUI(state) i = 0 while(True): print(state) eel.renderUI(state) # TO DO # save_waveform() # TO DO eel.sleep(1/state["pi_freq"]) if(i%100==0): clean_old_files("data/waveforms", 1000) i += 1 print(state)