1
0

main.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  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. })
  100. document.querySelector('#longGraphClear').addEventListener("click", function(event){
  101. eel.clean_long_term()
  102. })
  103. // Information for simple usage
  104. tippy('#boxAdress', {delay: 3000, content: "The i2c adress used for communication with the board. This is automatically obtained.",})
  105. tippy('#outOfLock', {delay: 3000, content: "The box is out of lock when the output of the board is maximum, and thus fluctuations cannot be stabilized anymore.",})
  106. tippy('#piFreq', {delay: 3000, content: "Frequency at which the Pi polls the board for new waveforms, typically no more than 10Hz as this can disturb the board.",})
  107. tippy('#boardFreq', {delay: 3000, content: "Frequency at which the board is currently measuring waveforms (untested).",})
  108. tippy('#remoteTrigg', {delay: 3000, content: "If true, the board will continously trigger, ignoring the trigger input. For normal operation set to false.",})
  109. tippy('#enabGnd', {delay: 3000, content: "If true (recommended), the board stabilizes signal-gnd, otherwise it stabilizes signal",})
  110. tippy('#Vlearn', {delay: 3000, 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.",})
  111. tippy('#start', {delay: 3000, content: "Point # where the signal starts being measured. Sampling frequency ~1MHz.",})
  112. tippy('#stop', {delay: 3000, content: "Point # where the signal stop being measured. Sampling frequency ~1MHz.",})
  113. tippy('#startGnd', {delay: 3000, content: "Point # where the ground starts being measured. Sampling frequency ~1MHz.",})
  114. tippy('#stopGnd', {delay: 3000, content: "Point # where the ground stops being measured. Sampling frequency ~1MHz.",})
  115. tippy('#N', {delay: 3000, content: "Number of waveforms (typ. 1) to sample in each stabilization cycle.",})
  116. tippy('#wfLen', {delay: 3000, content: "Number of points to sample (1-255)",})
  117. tippy('#stepMax', {delay: 3000, content: "Maximum step allowed in one stabilization cycle (typ. 200) . Small values may lead to slow response to sharp fluctuations.",})
  118. tippy('#Gain', {delay: 3000, content: "At every step Vout += (Vmeasured-Vset)*Gain",})
  119. tippy('#calibrateGain', {delay: 3000, content: "Automatically select a loop gain based on the response function of the system.",})
  120. tippy('#autoSetPga', {delay: 3000, 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.",})
  121. tippy('#inputGain', {delay: 3000, content: "Allowed values: [1, 2, 4, 8, 16, 32, 64, 128]",})
  122. tippy('#offset', {delay: 3000, content: "Input offset (0-4096). 2000 corresponds to no offset.",})
  123. tippy('#coarseFineRatio', {delay: 3000, content: "Ratio of the precision in the fine and broad outputs. Typ. 20 from circuit design.",})
  124. tippy('#calibrateCoarseFineRatio', {delay: 3000, content: "Calibrate the ratio by measuring the response functions of the broad and fine outputs.",})
  125. tippy('#measureResponseFunction', {delay: 3000, content: "Measure the photodiode signal as a funtion of the output voltage.",})
  126. setTimeout(loop, 100)
  127. }
  128. j = 0
  129. function loop(){
  130. if(document.getElementById("waveformGraphSelect") != document.activeElement){
  131. loadGraphList()
  132. }
  133. if(document.querySelector('#longGraphAutoupdate').checked){
  134. // Don't update this too often
  135. if(j%10==0){loadLongGraph()}
  136. }
  137. j++
  138. setTimeout(loop, 1000)
  139. }
  140. function renderUI(state){
  141. //console.log('Updating UI')
  142. // BOX INFORMATION
  143. tabs = document.querySelector('#tabs').children
  144. for (var i = 0; i < tabs.length; i++){
  145. tabs[i].classList.remove('active')
  146. if(i>=state.n_boards){
  147. tabs[i].style.opacity = 0
  148. }
  149. else{
  150. tabs[i].style.opacity = 1
  151. }
  152. }
  153. tabs[state.selected_board].classList.add('active')
  154. document.querySelector('#boxAddress').value = "0x"+state.box_address.toString(16)
  155. document.querySelector('#isLearn').checked = false
  156. document.querySelector('#isLock').checked = false
  157. if(state.mode == 0) document.querySelector('#isLearn').checked = true
  158. if(state.mode == 1) document.querySelector('#isLock').checked = true
  159. document.querySelector('#outOfLock').checked = false
  160. if(state.out_of_lock == 1) document.querySelector('#outOfLock').checked = true
  161. document.querySelector('#piFreq').value = state.pi_freq
  162. document.querySelector('#boardFreq').value = state.board_freq
  163. document.querySelector('#remoteTrigg').checked = false
  164. if(state.remote_trigg == 1) document.querySelector('#remoteTrigg').checked = true
  165. // LEARNING OPTIONS
  166. document.querySelector('#enabGnd').checked = state.enab_gnd
  167. document.querySelector('#Vlearn').value = state.Vlearn
  168. document.querySelector('#start').value = state.start
  169. document.querySelector('#stop').value = state.stop
  170. document.querySelector('#startGnd').value = state.start_gnd
  171. document.querySelector('#stopGnd').value = state.stop_gnd
  172. document.querySelector('#N').value = state.N
  173. document.querySelector('#wfLen').value = state.wf_len
  174. // LOCKING OPTIONS
  175. document.querySelector('#stepMax').value = state.step_max
  176. document.querySelector('#Gain').value = state.Gain
  177. // INPUT OPTIONS
  178. document.querySelector('#autoSetPga').checked = state.auto_set_pga
  179. document.querySelector('#inputGain').disabled = state.auto_set_pga==1
  180. document.querySelector('#offset').disabled = state.auto_set_pga==1
  181. document.querySelector('#inputGain').value = state.input_gain
  182. document.querySelector('#offset').value = state.offset
  183. // OUTPUT OPTIONS
  184. document.querySelector('#coarseFineRatio').value = state.coarse_fine_ratio
  185. }
  186. async function loadGraphList(){
  187. files = await eel.listFiles('data/waveforms/')()
  188. timestamps = files.map(s => parseInt(s.replace('.csv', '')))
  189. timestamps.sort()
  190. dates = []
  191. for(timestamp of timestamps){
  192. var d = new Date(0);
  193. d.setUTCMilliseconds(timestamp*10);
  194. dates.push(d.toLocaleString() + "." + d.getMilliseconds())
  195. }
  196. var list = document.getElementById("waveformGraphSelect")
  197. index = list.selectedIndex
  198. list.innerHTML = ""
  199. for (var i = 0; i < dates.length; i++){
  200. var element = document.createElement("option")
  201. element.setAttribute('timestamp', timestamps[i])
  202. element.innerText = dates[i]
  203. list.append(element);
  204. }
  205. list.selectedIndex = index
  206. if(document.querySelector('#waveformGraphAutoupdate').checked){
  207. list.selectedIndex = list.children.length-1
  208. if(list.children.length-1 != index) loadGraph()
  209. }
  210. else if(index>-1){
  211. list.selectedIndex = index
  212. }
  213. else{
  214. loadGraph()
  215. }
  216. }
  217. async function loadGraph(){
  218. option = document.querySelector('#waveformGraphSelect')[document.querySelector('#waveformGraphSelect').selectedIndex]
  219. timestamp = option.getAttribute('timestamp')
  220. csv = await eel.loadFile('data/waveforms/'+timestamp+'.csv')()
  221. document.querySelector('#waveformGraphText').innerText = "Signal: "+csv[8]+" - Gnd: "+csv[9]+" - Signal_std: "+csv[10].toFixed(2)+" - Gnd_std: "+csv[11].toFixed(2)
  222. var data = [{
  223. x: range(csv[12]),
  224. y: csv.slice(13),
  225. type: 'scatter'
  226. },{
  227. x: [csv[2], csv[2]],
  228. y: [0, Math.max(...csv.slice(13))],
  229. line: {
  230. color: 'red'
  231. },
  232. type: 'scatter',
  233. mode: 'lines'
  234. },{
  235. x: [csv[3], csv[3]],
  236. y: [0, Math.max(...csv.slice(13))],
  237. line: {
  238. color: 'red'
  239. },
  240. type: 'scatter',
  241. mode: 'lines'
  242. },{
  243. x: [csv[4], csv[4]],
  244. y: [0, Math.max(...csv.slice(13))],
  245. line: {
  246. color: 'green'
  247. },
  248. type: 'scatter',
  249. mode: 'lines'
  250. },{
  251. x: [csv[5], csv[5]],
  252. y: [0, Math.max(...csv.slice(13))],
  253. line: {
  254. color: 'green'
  255. },
  256. type: 'scatter',
  257. mode: 'lines'
  258. }];
  259. var layout = {
  260. margin: {
  261. l: 50,
  262. r: 10,
  263. b: 50,
  264. t: 10,
  265. pad: 4
  266. },
  267. xaxis: {
  268. title: 'Time',
  269. showgrid: true,
  270. },
  271. yaxis: {
  272. title: 'Input voltage',
  273. showgrid: true
  274. },
  275. showlegend: false
  276. };
  277. Plotly.newPlot('waveformGraph', data, layout);
  278. }
  279. async function loadLongGraph(){
  280. csv = await eel.loadLongTerm()()
  281. xIndex = parseInt(document.querySelector('#longGraphX').children[document.querySelector('#longGraphX').selectedIndex].value)
  282. xTitle = document.querySelector('#longGraphX').children[document.querySelector('#longGraphX').selectedIndex].innerText
  283. yIndex = parseInt(document.querySelector('#longGraphY').children[document.querySelector('#longGraphY').selectedIndex].value)
  284. yTitle = document.querySelector('#longGraphY').children[document.querySelector('#longGraphY').selectedIndex].innerText
  285. var data = [{
  286. x: csv.map(l => l[xIndex]),
  287. y: csv.map(l => l[yIndex]),
  288. type: 'scatter'
  289. }]
  290. if(xIndex==1){
  291. max = Math.max(...data[0].x)
  292. data[0].x = data[0].x.map(v => (v-max)/60)
  293. }
  294. var layout = {
  295. margin: {
  296. l: 50,
  297. r: 10,
  298. b: 50,
  299. t: 10,
  300. pad: 4
  301. },
  302. xaxis: {
  303. title: xTitle,
  304. showgrid: true,
  305. },
  306. yaxis: {
  307. title: yTitle,
  308. showgrid: true
  309. },
  310. showlegend: false
  311. };
  312. Plotly.newPlot('longGraph', data, layout);
  313. }
  314. function error(txt){
  315. Toastify({
  316. text: "Error: "+txt,
  317. gravity: 'bottom',
  318. position: 'left',
  319. close: true,
  320. backgroundColor: "#ef4041",
  321. duration: 3000,}).showToast()
  322. }
  323. function warning(txt){
  324. Toastify({
  325. text: "Warning: "+txt,
  326. gravity: 'bottom',
  327. position: 'left',
  328. close: true,
  329. backgroundColor: "#e36d25",
  330. duration: 3000,}).showToast()
  331. }
  332. function message(txt){
  333. Toastify({
  334. text: txt,
  335. gravity: 'bottom',
  336. position: 'left',
  337. close: true,
  338. backgroundColor: "white",
  339. duration: 3000,}).showToast()
  340. }