1
0

server.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. import time
  2. from struct import *
  3. import ctypes
  4. import array
  5. import numpy as np
  6. import matplotlib.pyplot as plt
  7. import os
  8. import eel # GUI
  9. from ADUCv2p1 import *
  10. ###################################
  11. ## PYTHON UTILITIES
  12. ###################################
  13. last_wf_number = 0
  14. last_timestamp = 0
  15. def save_waveform():
  16. global last_timestamp, last_wf_number
  17. chip = chips[state["selected_board"]]
  18. data = chip.read_data()
  19. gnd = sum(data[state["start_gnd"]:state["stop_gnd"]])/(state["stop_gnd"]-state["start_gnd"])
  20. signal = sum(data[state["start"]:state["stop"]])/(state["stop"]-state["start"])
  21. gnd_std = np.std(data[state["start_gnd"]:state["stop_gnd"]])
  22. signal_std = np.std(data[state["start"]:state["stop"]])
  23. wf_number = chip.get_wf_cnt()
  24. timestamp = time.time()
  25. if(last_wf_number>0 and wf_number - last_wf_number>0):
  26. state["board_freq"] = (wf_number - last_wf_number)/(timestamp - last_timestamp)
  27. else:
  28. state["board_freq"] = 0
  29. last_wf_number, last_timestamp = wf_number, timestamp
  30. 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)
  31. np.savetxt("data/waveforms/"+str(int(timestamp*100))+".csv", [tosave], delimiter=",", fmt='%10.5f')
  32. # Save more data for long term graphs
  33. tosave = [wf_number, timestamp, signal, gnd, signal-gnd, signal_std, gnd_std, chip.get_mode(), chip.get_out_of_lock(), chip.get_Vout(1), chip.get_Vout(2)]
  34. line = '\n'+','.join(map(str, tosave))
  35. with open('data/long_term.csv','a') as fd:
  36. fd.write(line)
  37. #update_state()
  38. import datetime
  39. @eel.expose
  40. def clean_old_files(path, max_Files):
  41. def sorted_ls(path):
  42. mtime = lambda f: os.stat(os.path.join(path, f)).st_mtime
  43. return list(sorted(os.listdir(path), key=mtime))
  44. del_list = sorted_ls(path)[0:(len(sorted_ls(path))-max_Files)]
  45. for dfile in del_list:
  46. os.remove(path + dfile)
  47. '''for dirpath, dirnames, filenames in os.walk("data/waveforms/"):
  48. for file in filenames:
  49. curpath = os.path.join(dirpath, file)
  50. file_modified = datetime.datetime.fromtimestamp(os.path.getmtime(curpath))
  51. if datetime.datetime.now() - file_modified > datetime.timedelta(hours=10):
  52. os.remove(curpath)'''
  53. @eel.expose
  54. def clean_long_term():
  55. with open('data/long_term.csv','w') as fd:
  56. fd.write('')
  57. @eel.expose
  58. def calibrate_gain():
  59. if(state["mode"]!=0):
  60. error("You must be in learn mode to calibrate the loop gain")
  61. return
  62. message("Calibrating...")
  63. chip = chips[state["selected_board"]]
  64. auto_set_pga = state["auto_set_pga"]
  65. chip.set_auto_set_pga(0) #Stop it while we scan
  66. measurements = []
  67. for Vfine in range(2000, 4000, 100):
  68. chip.set_Vout(2, Vfine)
  69. time.sleep(0.1)
  70. data = chip.read_data()
  71. gnd = sum(data[state["start_gnd"]:state["stop_gnd"]])/(state["stop_gnd"]-state["start_gnd"])
  72. signal = sum(data[state["start"]:state["stop"]])/(state["stop"]-state["start"])
  73. print('Vfine', chip.get_Vout(2), 'signal', signal-gnd)
  74. measurements.append([chip.get_Vout(2), signal-gnd])
  75. chip.set_Vout(2, 3000)
  76. measurements = np.array(measurements)
  77. p = np.polyfit(measurements[:,0], measurements[:,1], deg=1)
  78. print('signal-gnd vs Vfine slope', p[0], 'intercept', p[1])
  79. chip.set_Gain(1/p[0])
  80. print('Gain has been set to 1/slope', 1/p[0])
  81. plt.scatter(measurements[:,0], measurements[:,1], label='Measurements')
  82. plt.plot(np.arange(2000, 4000, 100), np.arange(2000, 4000, 100)*p[0] + p[1], label='Linear fit')
  83. plt.legend()
  84. plt.xlabel('Fine voltage')
  85. plt.ylabel('Photodiode signal')
  86. plt.show()
  87. chip.set_auto_set_pga(auto_set_pga)
  88. update_state()
  89. @eel.expose
  90. def calibrate_coarse_fine_ratio():
  91. if(state["mode"]!=0):
  92. error("You must be in learn mode to calibrate the broad to fine ratio")
  93. return
  94. message("Calibrating...")
  95. chip = chips[state["selected_board"]]
  96. auto_set_pga = state["auto_set_pga"]
  97. chip.set_auto_set_pga(0) #Stop it while we scan
  98. # Scan Vfine
  99. measurements = []
  100. for Vfine in range(2500, 3500, 100):
  101. chip.set_Vout(2, Vfine)
  102. time.sleep(0.1)
  103. data = chip.read_data()
  104. gnd = sum(data[state["start_gnd"]:state["stop_gnd"]])/(state["stop_gnd"]-state["start_gnd"])
  105. signal = sum(data[state["start"]:state["stop"]])/(state["stop"]-state["start"])
  106. print('Vfine', chip.get_Vout(2), 'signal', signal-gnd)
  107. measurements.append([chip.get_Vout(2), signal-gnd])
  108. chip.set_Vout(2, 3000)
  109. measurements = np.array(measurements)
  110. p = np.polyfit(measurements[:,0], measurements[:,1], deg=1)
  111. # Scan Vcoarse
  112. measurements = []
  113. low, high = state["Vlearn"]-100, state["Vlearn"]+100
  114. if(low<0):
  115. low=0
  116. if(high>4000):
  117. high=4000
  118. for Vcoarse in range(low, high, 20):
  119. chip.set_Vlearn(Vcoarse)
  120. time.sleep(0.1)
  121. data = chip.read_data()
  122. gnd = sum(data[state["start_gnd"]:state["stop_gnd"]])/(state["stop_gnd"]-state["start_gnd"])
  123. signal = sum(data[state["start"]:state["stop"]])/(state["stop"]-state["start"])
  124. print('Vcoarse', chip.get_Vlearn(), 'signal', signal-gnd)
  125. measurements.append([chip.get_Vlearn(), signal-gnd])
  126. chip.set_Vlearn(state["Vlearn"])
  127. measurements = np.array(measurements)
  128. q = np.polyfit(measurements[:,0], measurements[:,1], deg=1)
  129. # Set to ratio
  130. print('Setting coarse_fine_ratio to', q[0]/p[0])
  131. chip.set_coarse_fine_ratio(q[0]/p[0])
  132. chip.set_auto_set_pga(auto_set_pga)
  133. update_state()
  134. @eel.expose
  135. def measure_response_function():
  136. message("Measuring...")
  137. if(state["mode"]!=0):
  138. error("You must be in learn mode to measure the response function.")
  139. return
  140. chip = chips[state["selected_board"]]
  141. auto_set_pga = state["auto_set_pga"]
  142. chip.set_auto_set_pga(0) #Stop it while we scan
  143. # Scan Vcoarse
  144. measurements = []
  145. for Vcoarse in range(0, 4000, 100):
  146. chip.set_Vlearn(Vcoarse)
  147. time.sleep(0.1)
  148. data = chip.read_data()
  149. gnd = sum(data[state["start_gnd"]:state["stop_gnd"]])/(state["stop_gnd"]-state["start_gnd"])
  150. signal = sum(data[state["start"]:state["stop"]])/(state["stop"]-state["start"])
  151. print('Vcoarse', chip.get_Vlearn(), 'signal', signal-gnd)
  152. measurements.append([chip.get_Vlearn(), signal-gnd])
  153. chip.set_Vlearn(state["Vlearn"])
  154. measurements = np.array(measurements)
  155. plt.plot(measurements[:,0], measurements[:,1])
  156. plt.xlabel('Output voltage')
  157. plt.ylabel('Photodiode signal')
  158. plt.show()
  159. chip.set_auto_set_pga(auto_set_pga)
  160. update_state()
  161. @eel.expose
  162. def sdev_time():
  163. pass
  164. @eel.expose
  165. def noise_power_spectrum():
  166. pass
  167. ###################################
  168. ## EXPOSED GUI COMMUNICATION
  169. ###################################
  170. # Graphs
  171. from os import listdir
  172. @eel.expose
  173. def listFiles(path):
  174. return listdir(path)
  175. @eel.expose
  176. def loadFile(path):
  177. try:
  178. return list(np.loadtxt(path, delimiter=","))
  179. except:
  180. print('Could not load file', path)
  181. @eel.expose
  182. def loadLongTerm():
  183. try:
  184. li = list(np.loadtxt('data/long_term.csv', delimiter=","))
  185. 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
  186. with open('data/long_term.csv') as f, open("data/long_term_tmp.csv", "w") as out:
  187. for x in range(len(li)):
  188. if(len(li)-x>90000 or (li[-1][1]-li[x][1])/60/60 > 20):
  189. next(f)
  190. for line in f:
  191. out.write(line)
  192. os.remove("data/long_term.csv")
  193. os.rename("data/long_term_tmp.csv", "data/long_term.csv")
  194. return [list(l) for l in li]
  195. except:
  196. print('Couldnt load this')
  197. # Expose functions to GUI and do some parameter checking
  198. def error(txt):
  199. eel.error(txt)
  200. eel.renderUI(state)
  201. def warning(txt):
  202. eel.warning(txt)
  203. def message(txt):
  204. eel.message(txt)
  205. @eel.expose
  206. def set_selected_board(n):
  207. if n>len(chips):
  208. error("Selected board does not exist.")
  209. return;
  210. state["selected_board"] = n
  211. update_state()
  212. @eel.expose
  213. def set_pi_freq(freq):
  214. if(freq>100):
  215. error("The communication cannot be this fast. High values are likely to disturb the board.")
  216. return
  217. if(freq<0.01):
  218. error("Frequency must be at least 0.01Hz.")
  219. return
  220. if(freq>10):
  221. warning("High values are likely to disturb the board.")
  222. state["pi_freq"] = freq
  223. eel.renderUI(state)
  224. @eel.expose
  225. def set_remote_trigg(status):
  226. status = int(status)
  227. if status not in [0, 1]:
  228. error("remote_trigg must be set to either 0 or 1.")
  229. return
  230. chips[state["selected_board"]].set_remote_trigg(status)
  231. update_state()
  232. @eel.expose
  233. def set_enab_gnd(status):
  234. status = int(status)
  235. if status not in [0, 1]:
  236. error("enab_gnd must be set to either 0 or 1.")
  237. return
  238. chips[state["selected_board"]].set_enab_gnd(status)
  239. update_state()
  240. @eel.expose
  241. def set_Vlearn(Vlearn):
  242. if Vlearn>4095 or Vlearn<0:
  243. error("Vlearn must be in the range 0-4095")
  244. return
  245. if Vlearn<2000:
  246. warning("Low values of Vlearn will heavily attenuate the output. See the response curve.")
  247. if Vlearn>3500:
  248. 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.")
  249. chips[state["selected_board"]].set_Vlearn(Vlearn)
  250. update_state()
  251. @eel.expose
  252. def set_start(start):
  253. if start>255 or start<0:
  254. error("start must be in the range 0-256")
  255. return
  256. if start>state["stop"]:
  257. error("start must be lower than stop")
  258. return
  259. if start>state["wf_len"]:
  260. error("start must be lower than the waveform length")
  261. return
  262. chips[state["selected_board"]].set_start(start)
  263. update_state()
  264. @eel.expose
  265. def set_stop(stop):
  266. if stop>255 or stop<0:
  267. error("stop must be in the range 0-256")
  268. return
  269. if stop<state["start"]:
  270. error("start must be lower than stop")
  271. return
  272. if stop>state["wf_len"]:
  273. error("stop must be lower than the waveform length")
  274. return
  275. chips[state["selected_board"]].set_stop(stop)
  276. update_state()
  277. @eel.expose
  278. def set_start_gnd(start_gnd):
  279. if start_gnd>255 or start_gnd<0:
  280. error("start_gnd must be in the range 0-256")
  281. return
  282. if start_gnd>state["stop_gnd"]:
  283. error("start_gnd must be lower than stop")
  284. return
  285. if start_gnd>state["wf_len"]:
  286. error("start_gnd must be lower than the waveform length")
  287. return
  288. chips[state["selected_board"]].set_start_gnd(start_gnd)
  289. update_state()
  290. @eel.expose
  291. def set_stop_gnd(stop_gnd):
  292. if stop_gnd>255 or stop_gnd<0:
  293. error("stop_gnd must be in the range 0-256")
  294. return
  295. if stop_gnd<state["start_gnd"]:
  296. error("start must be lower than stop")
  297. return
  298. if stop_gnd>state["wf_len"]:
  299. error("stop_gnd must be lower than the waveform length")
  300. return
  301. chips[state["selected_board"]].set_stop_gnd(stop_gnd)
  302. update_state()
  303. @eel.expose
  304. def set_wf_len(wf_len):
  305. if wf_len>255 or wf_len<0:
  306. error("wf_len must be in the range 0-256")
  307. return
  308. if wf_len<state["stop"] or wf_len<state["stop_gnd"]:
  309. error("wf_len must be higher than both stop and stop_gnd")
  310. return
  311. chips[state["selected_board"]].set_wf_len(wf_len)
  312. update_state()
  313. @eel.expose
  314. def set_N(N):
  315. N = int(N)
  316. if N > 5:
  317. warning("Note that increasing the Number of waveforms per stabilization loop decreases the speed of the stabilization. We typically just have N=1.")
  318. chips[state["selected_board"]].set_N(N)
  319. update_state()
  320. @eel.expose
  321. def set_step_max(step):
  322. if step>4095 or step<1:
  323. error("step_max must be in the range 0-1")
  324. return
  325. if step<50:
  326. warning("step_max seems low. This may limit the ability to react to fast power fluctuations.")
  327. chips[state["selected_board"]].set_step_max(step)
  328. update_state()
  329. @eel.expose
  330. def set_Gain(*params):
  331. chips[state["selected_board"]].set_Gain(*params)
  332. update_state()
  333. @eel.expose
  334. def set_auto_set_pga(status):
  335. if status not in [0, 1]:
  336. error("auto_set_pga must be set to either 0 or 1.")
  337. return
  338. chips[state["selected_board"]].set_auto_set_pga(status)
  339. update_state()
  340. @eel.expose
  341. def set_input_gain(input_gain):
  342. if input_gain not in [1, 2, 4, 8, 16, 32, 64, 128]:
  343. error("input_gain must be one of [1, 2, 4, 8, 16, 32, 64, 128]")
  344. return
  345. chips[state["selected_board"]].set_input_gain(input_gain)
  346. update_state()
  347. @eel.expose
  348. def set_offset(offset):
  349. if offset>4095 or offset<0:
  350. error("offset must be in the range 0-4095. 2000 corresponds to no offset.")
  351. return
  352. chips[state["selected_board"]].set_offset(offset)
  353. update_state()
  354. @eel.expose
  355. def set_coarse_fine_ratio(coarse_fine_ratio):
  356. if coarse_fine_ratio<10 or coarse_fine_ratio>30:
  357. error("coarse_fine_ratio should be approximately 20.")
  358. return
  359. if coarse_fine_ratio<15 or coarse_fine_ratio>25:
  360. warning("coarse_fine_ratio should be approximately 20.")
  361. chips[state["selected_board"]].set_coarse_fine_ratio(coarse_fine_ratio)
  362. update_state()
  363. # Program state
  364. state = {
  365. "n_boards": 2,
  366. "selected_board": 0,
  367. "box_address": 0x50,
  368. "mode": 0,
  369. "out_of_lock": 0,
  370. "pi_freq": 1,
  371. "board_freq": -1,
  372. "remote_trigg": 0,
  373. "enab_gnd": 1,
  374. "Vlearn": 3200,
  375. "start": 25,
  376. "stop": 100,
  377. "start_gnd": 125,
  378. "stop_gnd": 200,
  379. "wf_len": 200,
  380. "N": 1,
  381. "step_max": 200,
  382. "Gain": 1.0,
  383. "auto_set_pga": 1,
  384. "input_gain": 1,
  385. "offset": 2000,
  386. "coarse_fine_ratio": 20
  387. }
  388. def update_state():
  389. state["n_boards"] = len(chips)
  390. state["box_address"] = adresses[state["selected_board"]]
  391. chip = chips[state["selected_board"]]
  392. state["enab_gnd"] = chip.get_enab_gnd()
  393. state["mode"] = chip.get_mode()
  394. state["out_of_lock"] = chip.get_out_of_lock()
  395. state["remote_trigg"] = chip.get_remote_trigg()
  396. state["Vlearn"] = chip.get_Vlearn()
  397. state["start"] = chip.get_start()
  398. state["stop"] = chip.get_stop()
  399. state["start_gnd"] = chip.get_start_gnd()
  400. state["stop_gnd"] = chip.get_stop_gnd()
  401. state["wf_len"] = chip.get_wf_len()
  402. state["N"] = chip.get_N()
  403. state["step_max"] = chip.get_step_max()
  404. state["Gain"] = chip.get_Gain()
  405. state["auto_set_pga"] = chip.get_auto_set_pga()
  406. state["input_gain"] = chip.get_input_gain()
  407. state["offset"] = chip.get_offset()
  408. state["coarse_fine_ratio"] = chip.get_coarse_fine_ratio()
  409. for l in state:
  410. if isinstance(state[l], np.uint16) or isinstance(state[l], np.int32):
  411. state[l] = int(state[l])
  412. if isinstance(state[l], np.float32):
  413. state[l] = float(state[l])
  414. eel.renderUI(state)
  415. ###################################
  416. ## START THE GUI
  417. ###################################
  418. my_options = {
  419. 'mode': "chrome-app", #chrome-app
  420. 'host': 'localhost',
  421. 'port': 8000 + int(np.random.rand()*1000),
  422. 'size':(660, 605),
  423. #'chromeFlags': ["--start-fullscreen", "--browser-startup-dialog"]
  424. }
  425. eel.init('web')
  426. eel.start('main.html', options=my_options, block=False)
  427. # Detect all boards
  428. import os
  429. import subprocess
  430. import re
  431. chips = []
  432. adresses = []
  433. # TO DO
  434. p = subprocess.Popen(['i2cdetect', '-y','1'],stdout=subprocess.PIPE,)
  435. p.stdout.readline()
  436. for i in range(0,8):
  437. line = str(p.stdout.readline())[4:]
  438. for match in re.finditer("[0-9a-f]+", line):
  439. adresses.append(int(match.group(0), 16))
  440. chips.append(ADUCv2p1(int(match.group(0), 16),True))
  441. print('Found boards', adresses, chips)
  442. eel.renderUI(state)
  443. eel.sleep(5)
  444. update_state()
  445. eel.renderUI(state)
  446. i = 0
  447. while(True):
  448. #eel.renderUI(state) # TO DO
  449. save_waveform() # TO DO
  450. eel.sleep(1/state["pi_freq"])
  451. if(i%10==0):
  452. eel.renderUI()
  453. if(i%100==0):
  454. clean_old_files("data/waveforms/", 1000)
  455. i += 1
  456. print(state)