1
0

main.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. eel.expose(renderUI);
  2. eel.expose(error)
  3. eel.expose(warning)
  4. eel.expose(message)
  5. // Helper functions
  6. function range(n){
  7. return [...Array(n).keys()];
  8. }
  9. function onload(){ //Bind events
  10. // Changing tabs
  11. tabs = document.querySelector('#tabs').children
  12. for (var i = 0; i < tabs.length; i++){
  13. tabs[i].addEventListener("click", function(event){
  14. eel.set_selected_board(event.srcElement.getAttribute("i"))
  15. })
  16. }
  17. // Box information
  18. document.querySelector('#piFreq').addEventListener("focusout", function(event){
  19. eel.set_pi_freq(parseFloat(event.srcElement.value))
  20. })
  21. document.querySelector('#remoteTrigg').addEventListener("click", function(event){
  22. eel.set_remote_trigg(event.srcElement.checked ? 1 : 0)
  23. })
  24. // Learning options
  25. document.querySelector('#enabGnd').addEventListener("click", function(event){
  26. eel.set_enab_gnd(event.srcElement.checked ? 1 : 0)
  27. })
  28. document.querySelector('#Vlearn').addEventListener("focusout", function(event){
  29. eel.set_Vlearn(parseInt(event.srcElement.value))
  30. })
  31. document.querySelector('#start').addEventListener("focusout", function(event){
  32. eel.set_start(parseInt(event.srcElement.value))
  33. })
  34. document.querySelector('#stop').addEventListener("focusout", function(event){
  35. eel.set_stop(parseInt(event.srcElement.value))
  36. })
  37. document.querySelector('#startGnd').addEventListener("focusout", function(event){
  38. eel.set_start_gnd(parseInt(event.srcElement.value))
  39. })
  40. document.querySelector('#stopGnd').addEventListener("focusout", function(event){
  41. eel.set_stop_gnd(parseInt(event.srcElement.value))
  42. })
  43. document.querySelector('#N').addEventListener("focusout", function(event){
  44. eel.set_N(parseInt(event.srcElement.value))
  45. })
  46. document.querySelector('#wfLen').addEventListener("focusout", function(event){
  47. eel.set_wf_len(parseInt(event.srcElement.value))
  48. })
  49. // Locking options
  50. document.querySelector('#stepMax').addEventListener("focusout", function(event){
  51. eel.set_max_step(parseInt(event.srcElement.value))
  52. })
  53. document.querySelector('#Gain').addEventListener("focusout", function(event){
  54. eel.set_Gain(parseFloat(event.srcElement.value))
  55. })
  56. document.querySelector('#calibrateGain').addEventListener("click", function(event){
  57. eel.calibrate_gain()
  58. })
  59. // Input options
  60. document.querySelector('#autoSetPga').addEventListener("click", function(event){
  61. eel.set_auto_set_pga(event.srcElement.checked ? 1 : 0)
  62. })
  63. document.querySelector('#inputGain').addEventListener("focusout", function(event){
  64. eel.set_input_gain(parseInt(event.srcElement.value))
  65. })
  66. document.querySelector('#offset').addEventListener("focusout", function(event){
  67. eel.set_offset(parseInt(event.srcElement.value))
  68. })
  69. // Output options
  70. document.querySelector('#coarseFineRatio').addEventListener("focusout", function(event){
  71. eel.set_coarse_fine_ratio(parseFloat(event.srcElement.value))
  72. })
  73. document.querySelector('#calibrateCoarseFineRatio').addEventListener("click", function(event){
  74. eel.calibrate_coarse_fine_ratio()
  75. })
  76. // Analysis tools
  77. document.querySelector('#measureResponseFunction').addEventListener("click", function(event){
  78. eel.measure_response_function()
  79. })
  80. document.querySelector('#sdevTime').addEventListener("click", function(event){
  81. eel.sdev_time()
  82. })
  83. document.querySelector('#noisePowerSpectrum').addEventListener("click", function(event){
  84. eel.noise_power_spectrum()
  85. })
  86. // Graphs
  87. document.getElementById("waveformGraphSelect").addEventListener("change", function(){
  88. document.querySelector('#waveformGraphAutoupdate').checked = false
  89. loadGraph()
  90. })
  91. document.getElementById("longGraphX").addEventListener("change", function(){
  92. loadLongGraph()
  93. })
  94. document.getElementById("longGraphY").addEventListener("change", function(){
  95. loadLongGraph()
  96. })
  97. document.querySelector('#waveformGraphClear').addEventListener("click", function(event){
  98. eel.clean_old_files("data/waveforms/", 0)
  99. loadGraphList()
  100. })
  101. document.querySelector('#longGraphClear').addEventListener("click", function(event){
  102. eel.clean_long_term()
  103. loadLongGraph()
  104. })
  105. // Information for simple usage
  106. tippy('#boxAdress', {delay: [1000, 0], content: "The i2c adress used for communication with the board. This is automatically obtained.",})
  107. tippy('#outOfLock', {delay: [1000, 0], content: "The box is out of lock when the output of the board is maximum, and thus fluctuations cannot be stabilized anymore.",})
  108. tippy('#piFreq', {delay: [1000, 0], content: "Frequency at which the Pi polls the board for new waveforms, typically no more than 10Hz as this can disturb the board.",})
  109. tippy('#boardFreq', {delay: [1000, 0], content: "Frequency at which the board is currently measuring waveforms (untested).",})
  110. tippy('#remoteTrigg', {delay: [1000, 0], content: "If true, the board will continously trigger, ignoring the trigger input. For normal operation set to false.",})
  111. tippy('#enabGnd', {delay: [1000, 0], content: "If true (recommended), the board stabilizes signal-gnd, otherwise it stabilizes signal",})
  112. tippy('#Vlearn', {delay: [1000, 0], content: "The output voltage (0-4095) when the box is in learn mode. This can be used to select an appropiate input power. Small values will unnecessarily suppress the signal and large values (>3500, see response function) may result in out_of_lock due to the limited range.",})
  113. tippy('#start', {delay: [1000, 0], content: "Point # where the signal starts being measured. Sampling frequency ~1MHz.",})
  114. tippy('#stop', {delay: [1000, 0], content: "Point # where the signal stop being measured. Sampling frequency ~1MHz.",})
  115. tippy('#startGnd', {delay: [1000, 0], content: "Point # where the ground starts being measured. Sampling frequency ~1MHz.",})
  116. tippy('#stopGnd', {delay: [1000, 0], content: "Point # where the ground stops being measured. Sampling frequency ~1MHz.",})
  117. tippy('#N', {delay: [1000, 0], content: "Number of waveforms (typ. 1) to sample in each stabilization cycle.",})
  118. tippy('#wfLen', {delay: [1000, 0], content: "Number of points to sample (1-255)",})
  119. tippy('#stepMax', {delay: [1000, 0], content: "Maximum step allowed in one stabilization cycle (typ. 200) . Small values may lead to slow response to sharp fluctuations.",})
  120. tippy('#Gain', {delay: [1000, 0], content: "At every step Vout += ((signal - ground) - Vset) * Gain",})
  121. tippy('#calibrateGain', {delay: [1000, 0], content: "Automatically select a loop gain based on the response function of the system.",})
  122. tippy('#autoSetPga', {delay: [1000, 0], content: "When True, the board changes the input gain and offset so that the signal is in an appropiate range (not clipped and going >2000). Set to False to select it manually.",})
  123. tippy('#inputGain', {delay: [1000, 0], content: "Allowed values: [1, 2, 4, 8, 16, 32, 64, 128]",})
  124. tippy('#offset', {delay: [1000, 0], content: "Input offset (0-4096). 2000 corresponds to no offset.",})
  125. tippy('#coarseFineRatio', {delay: [1000, 0], content: "Ratio of the precision in the fine and broad outputs. Typ. 20 from circuit design.",})
  126. tippy('#calibrateCoarseFineRatio', {delay: [1000, 0], content: "Calibrate the ratio by measuring the response functions of the broad and fine outputs.",})
  127. tippy('#measureResponseFunction', {delay: [1000, 0], content: "Measure the photodiode signal as a funtion of the output voltage.",})
  128. setTimeout(loop, 100)
  129. }
  130. j = 0
  131. function loop(){
  132. if(document.getElementById("waveformGraphSelect") != document.activeElement){
  133. loadGraphList()
  134. }
  135. if(document.querySelector('#longGraphAutoupdate').checked){
  136. // Don't update this too often
  137. if(j%10==0){loadLongGraph()}
  138. }
  139. j++
  140. setTimeout(loop, 1000)
  141. }
  142. function renderUI(state){
  143. //console.log('Updating UI')
  144. // BOX INFORMATION
  145. tabs = document.querySelector('#tabs').children
  146. for (var i = 0; i < tabs.length; i++){
  147. tabs[i].classList.remove('active')
  148. if(i>=state.n_boards){
  149. tabs[i].style.opacity = 0
  150. }
  151. else{
  152. tabs[i].style.opacity = 1
  153. }
  154. }
  155. tabs[state.selected_board].classList.add('active')
  156. document.querySelector('#boxAddress').value = "0x"+state.box_address.toString(16)
  157. document.querySelector('#isLearn').checked = false
  158. document.querySelector('#isLock').checked = false
  159. if(state.mode == 0) document.querySelector('#isLearn').checked = true
  160. if(state.mode == 1) document.querySelector('#isLock').checked = true
  161. document.querySelector('#outOfLock').checked = false
  162. if(state.out_of_lock == 1) document.querySelector('#outOfLock').checked = true
  163. document.querySelector('#piFreq').value = state.pi_freq
  164. document.querySelector('#boardFreq').value = state.board_freq
  165. document.querySelector('#remoteTrigg').checked = false
  166. if(state.remote_trigg == 1) document.querySelector('#remoteTrigg').checked = true
  167. // LEARNING OPTIONS
  168. document.querySelector('#enabGnd').checked = state.enab_gnd
  169. document.querySelector('#Vlearn').value = state.Vlearn
  170. document.querySelector('#start').value = state.start
  171. document.querySelector('#stop').value = state.stop
  172. document.querySelector('#startGnd').value = state.start_gnd
  173. document.querySelector('#stopGnd').value = state.stop_gnd
  174. document.querySelector('#N').value = state.N
  175. document.querySelector('#wfLen').value = state.wf_len
  176. // LOCKING OPTIONS
  177. document.querySelector('#stepMax').value = state.step_max
  178. document.querySelector('#Gain').value = state.Gain
  179. // INPUT OPTIONS
  180. document.querySelector('#autoSetPga').checked = state.auto_set_pga
  181. document.querySelector('#inputGain').disabled = state.auto_set_pga==1
  182. document.querySelector('#offset').disabled = state.auto_set_pga==1
  183. document.querySelector('#inputGain').value = state.input_gain
  184. document.querySelector('#offset').value = state.offset
  185. // OUTPUT OPTIONS
  186. document.querySelector('#coarseFineRatio').value = state.coarse_fine_ratio
  187. }
  188. async function loadGraphList(){
  189. files = await eel.listFiles('data/waveforms/')()
  190. timestamps = files.map(s => parseInt(s.replace('.csv', '')))
  191. timestamps.sort()
  192. dates = []
  193. for(timestamp of timestamps){
  194. var d = new Date(0);
  195. d.setUTCMilliseconds(timestamp*10);
  196. dates.push(d.toLocaleString() + "." + d.getMilliseconds())
  197. }
  198. var list = document.getElementById("waveformGraphSelect")
  199. index = list.selectedIndex
  200. list.innerHTML = ""
  201. for (var i = 0; i < dates.length; i++){
  202. var element = document.createElement("option")
  203. element.setAttribute('timestamp', timestamps[i])
  204. element.innerText = dates[i]
  205. list.append(element);
  206. }
  207. list.selectedIndex = index
  208. if(document.querySelector('#waveformGraphAutoupdate').checked){
  209. list.selectedIndex = list.children.length-1
  210. if(list.children.length-1 != index) loadGraph()
  211. }
  212. else if(index>-1){
  213. list.selectedIndex = index
  214. }
  215. else{
  216. loadGraph()
  217. }
  218. }
  219. async function loadGraph(){
  220. option = document.querySelector('#waveformGraphSelect')[document.querySelector('#waveformGraphSelect').selectedIndex]
  221. timestamp = option.getAttribute('timestamp')
  222. csv = await eel.loadFile('data/waveforms/'+timestamp+'.csv')()
  223. document.querySelector('#waveformGraphText').innerText = "Signal: "+csv[8]+" - Gnd: "+csv[9]+" - Signal_std: "+csv[10].toFixed(2)+" - Gnd_std: "+csv[11].toFixed(2)
  224. var data = [{
  225. x: range(csv[12]),
  226. y: csv.slice(13),
  227. type: 'scatter'
  228. },{
  229. x: [csv[2], csv[2]],
  230. y: [0, Math.max(...csv.slice(13))],
  231. line: {
  232. color: 'red'
  233. },
  234. type: 'scatter',
  235. mode: 'lines'
  236. },{
  237. x: [csv[3], csv[3]],
  238. y: [0, Math.max(...csv.slice(13))],
  239. line: {
  240. color: 'red'
  241. },
  242. type: 'scatter',
  243. mode: 'lines'
  244. },{
  245. x: [csv[4], csv[4]],
  246. y: [0, Math.max(...csv.slice(13))],
  247. line: {
  248. color: 'green'
  249. },
  250. type: 'scatter',
  251. mode: 'lines'
  252. },{
  253. x: [csv[5], csv[5]],
  254. y: [0, Math.max(...csv.slice(13))],
  255. line: {
  256. color: 'green'
  257. },
  258. type: 'scatter',
  259. mode: 'lines'
  260. }];
  261. var layout = {
  262. margin: {
  263. l: 50,
  264. r: 10,
  265. b: 50,
  266. t: 10,
  267. pad: 4
  268. },
  269. xaxis: {
  270. title: 'Time',
  271. showgrid: true,
  272. },
  273. yaxis: {
  274. title: 'Input voltage',
  275. showgrid: true
  276. },
  277. showlegend: false
  278. };
  279. Plotly.newPlot('waveformGraph', data, layout);
  280. }
  281. async function loadLongGraph(){
  282. csv = await eel.loadLongTerm()()
  283. xIndex = parseInt(document.querySelector('#longGraphX').children[document.querySelector('#longGraphX').selectedIndex].value)
  284. xTitle = document.querySelector('#longGraphX').children[document.querySelector('#longGraphX').selectedIndex].innerText
  285. yIndex = parseInt(document.querySelector('#longGraphY').children[document.querySelector('#longGraphY').selectedIndex].value)
  286. yTitle = document.querySelector('#longGraphY').children[document.querySelector('#longGraphY').selectedIndex].innerText
  287. var data = [{
  288. x: csv.map(l => l[xIndex]),
  289. y: csv.map(l => l[yIndex]),
  290. type: 'scatter'
  291. }]
  292. if(xIndex==1){
  293. max = Math.max(...data[0].x)
  294. data[0].x = data[0].x.map(v => (v-max)/60)
  295. }
  296. var layout = {
  297. margin: {
  298. l: 50,
  299. r: 10,
  300. b: 50,
  301. t: 10,
  302. pad: 4
  303. },
  304. xaxis: {
  305. title: xTitle,
  306. showgrid: true,
  307. },
  308. yaxis: {
  309. title: yTitle,
  310. showgrid: true
  311. },
  312. showlegend: false
  313. };
  314. Plotly.newPlot('longGraph', data, layout);
  315. }
  316. function error(txt){
  317. Toastify({
  318. text: "Error: "+txt,
  319. gravity: 'bottom',
  320. position: 'left',
  321. close: true,
  322. backgroundColor: "#ef4041",
  323. duration: 3000,}).showToast()
  324. }
  325. function warning(txt){
  326. Toastify({
  327. text: "Warning: "+txt,
  328. gravity: 'bottom',
  329. position: 'left',
  330. close: true,
  331. backgroundColor: "#e36d25",
  332. duration: 3000,}).showToast()
  333. }
  334. function message(txt){
  335. Toastify({
  336. text: txt,
  337. gravity: 'bottom',
  338. position: 'left',
  339. close: true,
  340. backgroundColor: "grey",
  341. duration: 3000,}).showToast()
  342. }