eel.expose(renderUI); eel.expose(error) eel.expose(warning) eel.expose(message) // Helper functions function range(n){ return [...Array(n).keys()]; } function onload(){ //Bind events // Changing tabs tabs = document.querySelector('#tabs').children for (var i = 0; i < tabs.length; i++){ tabs[i].addEventListener("click", function(event){ eel.set_selected_board(event.srcElement.getAttribute("i")) }) } // Box information document.querySelector('#piFreq').addEventListener("focusout", function(event){ eel.set_pi_freq(parseFloat(event.srcElement.value)) }) document.querySelector('#remoteTrigg').addEventListener("click", function(event){ eel.set_remote_trigg(event.srcElement.checked ? 1 : 0) }) // Learning options document.querySelector('#enabGnd').addEventListener("click", function(event){ eel.set_enab_gnd(event.srcElement.checked ? 1 : 0) }) document.querySelector('#Vlearn').addEventListener("focusout", function(event){ eel.set_Vlearn(parseInt(event.srcElement.value)) }) document.querySelector('#start').addEventListener("focusout", function(event){ eel.set_start(parseInt(event.srcElement.value)) }) document.querySelector('#stop').addEventListener("focusout", function(event){ eel.set_stop(parseInt(event.srcElement.value)) }) document.querySelector('#startGnd').addEventListener("focusout", function(event){ eel.set_start_gnd(parseInt(event.srcElement.value)) }) document.querySelector('#stopGnd').addEventListener("focusout", function(event){ eel.set_stop_gnd(parseInt(event.srcElement.value)) }) document.querySelector('#N').addEventListener("focusout", function(event){ eel.set_N(parseInt(event.srcElement.value)) }) document.querySelector('#wfLen').addEventListener("focusout", function(event){ eel.set_wf_len(parseInt(event.srcElement.value)) }) // Locking options document.querySelector('#stepMax').addEventListener("focusout", function(event){ eel.set_max_step(parseInt(event.srcElement.value)) }) document.querySelector('#Gain').addEventListener("focusout", function(event){ eel.set_Gain(parseFloat(event.srcElement.value)) }) document.querySelector('#calibrateGain').addEventListener("click", function(event){ eel.calibrate_gain() }) // Input options document.querySelector('#autoSetPga').addEventListener("click", function(event){ eel.set_auto_set_pga(event.srcElement.checked ? 1 : 0) }) document.querySelector('#inputGain').addEventListener("focusout", function(event){ eel.set_input_gain(parseInt(event.srcElement.value)) }) document.querySelector('#offset').addEventListener("focusout", function(event){ eel.set_offset(parseInt(event.srcElement.value)) }) // Output options document.querySelector('#coarseFineRatio').addEventListener("focusout", function(event){ eel.set_coarse_fine_ratio(parseFloat(event.srcElement.value)) }) document.querySelector('#calibrateCoarseFineRatio').addEventListener("click", function(event){ eel.calibrate_coarse_fine_ratio() }) // Analysis tools document.querySelector('#measureResponseFunction').addEventListener("click", function(event){ eel.measure_response_function() }) document.querySelector('#sdevTime').addEventListener("click", function(event){ eel.sdev_time() }) document.querySelector('#noisePowerSpectrum').addEventListener("click", function(event){ eel.noise_power_spectrum() }) // Graphs document.getElementById("waveformGraphSelect").addEventListener("change", function(){ document.querySelector('#waveformGraphAutoupdate').checked = false loadGraph() }) document.getElementById("longGraphX").addEventListener("change", function(){ loadLongGraph() }) document.getElementById("longGraphY").addEventListener("change", function(){ loadLongGraph() }) document.querySelector('#waveformGraphClear').addEventListener("click", function(event){ eel.clean_old_files("data/waveforms/", 0) loadGraphList() }) document.querySelector('#longGraphClear').addEventListener("click", function(event){ eel.clean_long_term() loadLongGraph() }) // Information for simple usage tippy('#boxAdress', {delay: [1000, 0], content: "The i2c adress used for communication with the board. This is automatically obtained.",}) 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.",}) 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.",}) tippy('#boardFreq', {delay: [1000, 0], content: "Frequency at which the board is currently measuring waveforms (untested).",}) tippy('#remoteTrigg', {delay: [1000, 0], content: "If true, the board will continously trigger, ignoring the trigger input. For normal operation set to false.",}) tippy('#enabGnd', {delay: [1000, 0], content: "If true (recommended), the board stabilizes signal-gnd, otherwise it stabilizes signal",}) 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.",}) tippy('#start', {delay: [1000, 0], content: "Point # where the signal starts being measured. Sampling frequency ~1MHz.",}) tippy('#stop', {delay: [1000, 0], content: "Point # where the signal stop being measured. Sampling frequency ~1MHz.",}) tippy('#startGnd', {delay: [1000, 0], content: "Point # where the ground starts being measured. Sampling frequency ~1MHz.",}) tippy('#stopGnd', {delay: [1000, 0], content: "Point # where the ground stops being measured. Sampling frequency ~1MHz.",}) tippy('#N', {delay: [1000, 0], content: "Number of waveforms (typ. 1) to sample in each stabilization cycle.",}) tippy('#wfLen', {delay: [1000, 0], content: "Number of points to sample (1-255)",}) 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.",}) tippy('#Gain', {delay: [1000, 0], content: "At every step Vout += (Vmeasured-Vset)*Gain",}) tippy('#calibrateGain', {delay: [1000, 0], content: "Automatically select a loop gain based on the response function of the system.",}) 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.",}) tippy('#inputGain', {delay: [1000, 0], content: "Allowed values: [1, 2, 4, 8, 16, 32, 64, 128]",}) tippy('#offset', {delay: [1000, 0], content: "Input offset (0-4096). 2000 corresponds to no offset.",}) tippy('#coarseFineRatio', {delay: [1000, 0], content: "Ratio of the precision in the fine and broad outputs. Typ. 20 from circuit design.",}) tippy('#calibrateCoarseFineRatio', {delay: [1000, 0], content: "Calibrate the ratio by measuring the response functions of the broad and fine outputs.",}) tippy('#measureResponseFunction', {delay: [1000, 0], content: "Measure the photodiode signal as a funtion of the output voltage.",}) setTimeout(loop, 100) } j = 0 function loop(){ if(document.getElementById("waveformGraphSelect") != document.activeElement){ loadGraphList() } if(document.querySelector('#longGraphAutoupdate').checked){ // Don't update this too often if(j%10==0){loadLongGraph()} } j++ setTimeout(loop, 1000) } function renderUI(state){ //console.log('Updating UI') // BOX INFORMATION tabs = document.querySelector('#tabs').children for (var i = 0; i < tabs.length; i++){ tabs[i].classList.remove('active') if(i>=state.n_boards){ tabs[i].style.opacity = 0 } else{ tabs[i].style.opacity = 1 } } tabs[state.selected_board].classList.add('active') document.querySelector('#boxAddress').value = "0x"+state.box_address.toString(16) document.querySelector('#isLearn').checked = false document.querySelector('#isLock').checked = false if(state.mode == 0) document.querySelector('#isLearn').checked = true if(state.mode == 1) document.querySelector('#isLock').checked = true document.querySelector('#outOfLock').checked = false if(state.out_of_lock == 1) document.querySelector('#outOfLock').checked = true document.querySelector('#piFreq').value = state.pi_freq document.querySelector('#boardFreq').value = state.board_freq document.querySelector('#remoteTrigg').checked = false if(state.remote_trigg == 1) document.querySelector('#remoteTrigg').checked = true // LEARNING OPTIONS document.querySelector('#enabGnd').checked = state.enab_gnd document.querySelector('#Vlearn').value = state.Vlearn document.querySelector('#start').value = state.start document.querySelector('#stop').value = state.stop document.querySelector('#startGnd').value = state.start_gnd document.querySelector('#stopGnd').value = state.stop_gnd document.querySelector('#N').value = state.N document.querySelector('#wfLen').value = state.wf_len // LOCKING OPTIONS document.querySelector('#stepMax').value = state.step_max document.querySelector('#Gain').value = state.Gain // INPUT OPTIONS document.querySelector('#autoSetPga').checked = state.auto_set_pga document.querySelector('#inputGain').disabled = state.auto_set_pga==1 document.querySelector('#offset').disabled = state.auto_set_pga==1 document.querySelector('#inputGain').value = state.input_gain document.querySelector('#offset').value = state.offset // OUTPUT OPTIONS document.querySelector('#coarseFineRatio').value = state.coarse_fine_ratio } async function loadGraphList(){ files = await eel.listFiles('data/waveforms/')() timestamps = files.map(s => parseInt(s.replace('.csv', ''))) timestamps.sort() dates = [] for(timestamp of timestamps){ var d = new Date(0); d.setUTCMilliseconds(timestamp*10); dates.push(d.toLocaleString() + "." + d.getMilliseconds()) } var list = document.getElementById("waveformGraphSelect") index = list.selectedIndex list.innerHTML = "" for (var i = 0; i < dates.length; i++){ var element = document.createElement("option") element.setAttribute('timestamp', timestamps[i]) element.innerText = dates[i] list.append(element); } list.selectedIndex = index if(document.querySelector('#waveformGraphAutoupdate').checked){ list.selectedIndex = list.children.length-1 if(list.children.length-1 != index) loadGraph() } else if(index>-1){ list.selectedIndex = index } else{ loadGraph() } } async function loadGraph(){ option = document.querySelector('#waveformGraphSelect')[document.querySelector('#waveformGraphSelect').selectedIndex] timestamp = option.getAttribute('timestamp') csv = await eel.loadFile('data/waveforms/'+timestamp+'.csv')() document.querySelector('#waveformGraphText').innerText = "Signal: "+csv[8]+" - Gnd: "+csv[9]+" - Signal_std: "+csv[10].toFixed(2)+" - Gnd_std: "+csv[11].toFixed(2) var data = [{ x: range(csv[12]), y: csv.slice(13), type: 'scatter' },{ x: [csv[2], csv[2]], y: [0, Math.max(...csv.slice(13))], line: { color: 'red' }, type: 'scatter', mode: 'lines' },{ x: [csv[3], csv[3]], y: [0, Math.max(...csv.slice(13))], line: { color: 'red' }, type: 'scatter', mode: 'lines' },{ x: [csv[4], csv[4]], y: [0, Math.max(...csv.slice(13))], line: { color: 'green' }, type: 'scatter', mode: 'lines' },{ x: [csv[5], csv[5]], y: [0, Math.max(...csv.slice(13))], line: { color: 'green' }, type: 'scatter', mode: 'lines' }]; var layout = { margin: { l: 50, r: 10, b: 50, t: 10, pad: 4 }, xaxis: { title: 'Time', showgrid: true, }, yaxis: { title: 'Input voltage', showgrid: true }, showlegend: false }; Plotly.newPlot('waveformGraph', data, layout); } async function loadLongGraph(){ csv = await eel.loadLongTerm()() xIndex = parseInt(document.querySelector('#longGraphX').children[document.querySelector('#longGraphX').selectedIndex].value) xTitle = document.querySelector('#longGraphX').children[document.querySelector('#longGraphX').selectedIndex].innerText yIndex = parseInt(document.querySelector('#longGraphY').children[document.querySelector('#longGraphY').selectedIndex].value) yTitle = document.querySelector('#longGraphY').children[document.querySelector('#longGraphY').selectedIndex].innerText var data = [{ x: csv.map(l => l[xIndex]), y: csv.map(l => l[yIndex]), type: 'scatter' }] if(xIndex==1){ max = Math.max(...data[0].x) data[0].x = data[0].x.map(v => (v-max)/60) } var layout = { margin: { l: 50, r: 10, b: 50, t: 10, pad: 4 }, xaxis: { title: xTitle, showgrid: true, }, yaxis: { title: yTitle, showgrid: true }, showlegend: false }; Plotly.newPlot('longGraph', data, layout); } function error(txt){ Toastify({ text: "Error: "+txt, gravity: 'bottom', position: 'left', close: true, backgroundColor: "#ef4041", duration: 3000,}).showToast() } function warning(txt){ Toastify({ text: "Warning: "+txt, gravity: 'bottom', position: 'left', close: true, backgroundColor: "#e36d25", duration: 3000,}).showToast() } function message(txt){ Toastify({ text: txt, gravity: 'bottom', position: 'left', close: true, backgroundColor: "grey", duration: 3000,}).showToast() }