server.py 14 KB

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