main.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  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. if(document.activeElement.tagName == "INPUT"){
  144. return;
  145. }
  146. //console.log('Updating UI')
  147. // BOX INFORMATION
  148. tabs = document.querySelector('#tabs').children
  149. for (var i = 0; i < tabs.length; i++){
  150. tabs[i].classList.remove('active')
  151. if(i>=state.n_boards){
  152. tabs[i].style.opacity = 0
  153. }
  154. else{
  155. tabs[i].style.opacity = 1
  156. }
  157. }
  158. tabs[state.selected_board].classList.add('active')
  159. document.querySelector('#boxAddress').value = "0x"+state.box_address.toString(16)
  160. document.querySelector('#isLearn').checked = false
  161. document.querySelector('#isLock').checked = false
  162. if(state.mode == 0) document.querySelector('#isLearn').checked = true
  163. if(state.mode == 1) document.querySelector('#isLock').checked = true
  164. document.querySelector('#outOfLock').checked = false
  165. if(state.out_of_lock == 1) document.querySelector('#outOfLock').checked = true
  166. document.querySelector('#piFreq').value = state.pi_freq
  167. document.querySelector('#boardFreq').value = state.board_freq
  168. document.querySelector('#remoteTrigg').checked = false
  169. if(state.remote_trigg == 1) document.querySelector('#remoteTrigg').checked = true
  170. // LEARNING OPTIONS
  171. document.querySelector('#enabGnd').checked = state.enab_gnd
  172. document.querySelector('#Vlearn').value = state.Vlearn
  173. document.querySelector('#start').value = state.start
  174. document.querySelector('#stop').value = state.stop
  175. document.querySelector('#startGnd').value = state.start_gnd
  176. document.querySelector('#stopGnd').value = state.stop_gnd
  177. document.querySelector('#N').value = state.N
  178. document.querySelector('#wfLen').value = state.wf_len
  179. // LOCKING OPTIONS
  180. document.querySelector('#stepMax').value = state.step_max
  181. document.querySelector('#Gain').value = state.Gain
  182. // INPUT OPTIONS
  183. document.querySelector('#autoSetPga').checked = state.auto_set_pga
  184. document.querySelector('#inputGain').disabled = state.auto_set_pga==1
  185. document.querySelector('#offset').disabled = state.auto_set_pga==1
  186. document.querySelector('#inputGain').value = state.input_gain
  187. document.querySelector('#offset').value = state.offset
  188. // OUTPUT OPTIONS
  189. document.querySelector('#coarseFineRatio').value = state.coarse_fine_ratio
  190. }
  191. async function loadGraphList(){
  192. files = await eel.listFiles('data/waveforms/')()
  193. timestamps = files.map(s => parseInt(s.replace('.csv', '')))
  194. timestamps.sort()
  195. dates = []
  196. for(timestamp of timestamps){
  197. var d = new Date(0);
  198. d.setUTCMilliseconds(timestamp*10);
  199. dates.push(d.toLocaleString() + "." + d.getMilliseconds())
  200. }
  201. var list = document.getElementById("waveformGraphSelect")
  202. index = list.selectedIndex
  203. list.innerHTML = ""
  204. for (var i = 0; i < dates.length; i++){
  205. var element = document.createElement("option")
  206. element.setAttribute('timestamp', timestamps[i])
  207. element.innerText = dates[i]
  208. list.append(element);
  209. }
  210. list.selectedIndex = index
  211. if(document.querySelector('#waveformGraphAutoupdate').checked){
  212. list.selectedIndex = list.children.length-1
  213. if(list.children.length-1 != index) loadGraph()
  214. }
  215. else if(index>-1){
  216. list.selectedIndex = index
  217. }
  218. else{
  219. loadGraph()
  220. }
  221. }
  222. async function loadGraph(){
  223. option = document.querySelector('#waveformGraphSelect')[document.querySelector('#waveformGraphSelect').selectedIndex]
  224. timestamp = option.getAttribute('timestamp')
  225. csv = await eel.loadFile('data/waveforms/'+timestamp+'.csv')()
  226. document.querySelector('#waveformGraphText').innerText = "Signal: "+csv[8]+" - Gnd: "+csv[9]+" - Signal_std: "+csv[10].toFixed(2)+" - Gnd_std: "+csv[11].toFixed(2)
  227. var data = [{
  228. x: range(csv[12]),
  229. y: csv.slice(13),
  230. type: 'scatter'
  231. },{
  232. x: [csv[2], csv[2]],
  233. y: [0, Math.max(...csv.slice(13))],
  234. line: {
  235. color: 'red'
  236. },
  237. type: 'scatter',
  238. mode: 'lines'
  239. },{
  240. x: [csv[3], csv[3]],
  241. y: [0, Math.max(...csv.slice(13))],
  242. line: {
  243. color: 'red'
  244. },
  245. type: 'scatter',
  246. mode: 'lines'
  247. },{
  248. x: [csv[4], csv[4]],
  249. y: [0, Math.max(...csv.slice(13))],
  250. line: {
  251. color: 'green'
  252. },
  253. type: 'scatter',
  254. mode: 'lines'
  255. },{
  256. x: [csv[5], csv[5]],
  257. y: [0, Math.max(...csv.slice(13))],
  258. line: {
  259. color: 'green'
  260. },
  261. type: 'scatter',
  262. mode: 'lines'
  263. }];
  264. var layout = {
  265. margin: {
  266. l: 50,
  267. r: 10,
  268. b: 50,
  269. t: 10,
  270. pad: 4
  271. },
  272. xaxis: {
  273. title: 'Time',
  274. showgrid: true,
  275. },
  276. yaxis: {
  277. title: 'Input voltage',
  278. showgrid: true
  279. },
  280. showlegend: false
  281. };
  282. Plotly.newPlot('waveformGraph', data, layout);
  283. }
  284. async function loadLongGraph(){
  285. csv = await eel.loadLongTerm()()
  286. xIndex = parseInt(document.querySelector('#longGraphX').children[document.querySelector('#longGraphX').selectedIndex].value)
  287. xTitle = document.querySelector('#longGraphX').children[document.querySelector('#longGraphX').selectedIndex].innerText
  288. yIndex = parseInt(document.querySelector('#longGraphY').children[document.querySelector('#longGraphY').selectedIndex].value)
  289. yTitle = document.querySelector('#longGraphY').children[document.querySelector('#longGraphY').selectedIndex].innerText
  290. var data = [{
  291. x: csv.map(l => l[xIndex]),
  292. y: csv.map(l => l[yIndex]),
  293. type: 'scatter'
  294. }]
  295. if(xIndex==1){
  296. max = Math.max(...data[0].x)
  297. data[0].x = data[0].x.map(v => (v-max)/60)
  298. }
  299. var layout = {
  300. margin: {
  301. l: 50,
  302. r: 10,
  303. b: 50,
  304. t: 10,
  305. pad: 4
  306. },
  307. xaxis: {
  308. title: xTitle,
  309. showgrid: true,
  310. },
  311. yaxis: {
  312. title: yTitle,
  313. showgrid: true
  314. },
  315. showlegend: false
  316. };
  317. Plotly.newPlot('longGraph', data, layout);
  318. }
  319. function error(txt){
  320. Toastify({
  321. text: "Error: "+txt,
  322. gravity: 'bottom',
  323. position: 'left',
  324. close: true,
  325. backgroundColor: "#ef4041",
  326. duration: 3000,}).showToast()
  327. }
  328. function warning(txt){
  329. Toastify({
  330. text: "Warning: "+txt,
  331. gravity: 'bottom',
  332. position: 'left',
  333. close: true,
  334. backgroundColor: "#e36d25",
  335. duration: 3000,}).showToast()
  336. }
  337. function message(txt){
  338. Toastify({
  339. text: txt,
  340. gravity: 'bottom',
  341. position: 'left',
  342. close: true,
  343. backgroundColor: "grey",
  344. duration: 3000,}).showToast()
  345. }