server.py 12 KB

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