diff --git a/build_examples.sh b/build_examples.sh index 8f4386155..589862eae 100644 --- a/build_examples.sh +++ b/build_examples.sh @@ -8,6 +8,7 @@ make CONFIG=examples/alpha250/fft/config.yml MODE=$mode $target make CONFIG=examples/alpha250/loopback/config.yml MODE=$mode $target make CONFIG=examples/alpha250/adc-dac-bram/config.yml MODE=$mode $target make CONFIG=examples/alpha250/adc-dac-dma/config.yml MODE=$mode $target +make CONFIG=examples/alpha250/phase-noise-analyzer/config.yml MODE=$mode $target make CONFIG=examples/red-pitaya/led-blinker/config.yml MODE=$mode $target make CONFIG=examples/red-pitaya/oscillo/config.yml MODE=$mode $target make CONFIG=examples/red-pitaya/spectrum/config.yml MODE=$mode $target @@ -18,5 +19,6 @@ make CONFIG=examples/red-pitaya/adc-dac/config.yml MODE=$mode $target make CONFIG=examples/red-pitaya/cluster/config.yml MODE=$mode $target make CONFIG=examples/red-pitaya/dual-dds/config.yml MODE=$mode $target make CONFIG=examples/red-pitaya/fft/config.yml MODE=$mode $target +make CONFIG=examples/red-pitaya/phase-noise-analyzer/config.yml MODE=$mode $target make CONFIG=examples/zedboard/led-blinker/config.yml MODE=$mode $target make CONFIG=examples/zedboard/picoblaze/config.yml MODE=$mode $target diff --git a/examples/alpha250/adc-dac-bram/config.yml b/examples/alpha250/adc-dac-bram/config.yml index 3683569ba..4ebe89521 100644 --- a/examples/alpha250/adc-dac-bram/config.yml +++ b/examples/alpha250/adc-dac-bram/config.yml @@ -1,7 +1,7 @@ --- name: adc-dac-bram board: boards/alpha250 -version: 0.1.0 +version: 0.1.1 cores: - fpga/cores/axi_ctl_register_v1_0 @@ -66,4 +66,4 @@ drivers: web: - web/index.html - web/main.css - - web/navigation.ts + - web/koheron.ts \ No newline at end of file diff --git a/examples/alpha250/adc-dac-dma/config.yml b/examples/alpha250/adc-dac-dma/config.yml index 1df93af33..5ccfd4a3c 100644 --- a/examples/alpha250/adc-dac-dma/config.yml +++ b/examples/alpha250/adc-dac-dma/config.yml @@ -1,7 +1,7 @@ --- name: adc-dac-dma board: boards/alpha250 -version: 0.1.0 +version: 0.1.1 cores: - fpga/cores/axi_ctl_register_v1_0 @@ -90,5 +90,4 @@ drivers: web: - web/index.html - web/main.css - - web/navigation.ts - + - web/koheron.ts \ No newline at end of file diff --git a/examples/alpha250/fft/config.yml b/examples/alpha250/fft/config.yml index 00901ec2d..b42937a69 100644 --- a/examples/alpha250/fft/config.yml +++ b/examples/alpha250/fft/config.yml @@ -1,7 +1,7 @@ --- name: fft board: boards/alpha250 -version: 0.1.0 +version: 0.1.1 cores: - fpga/cores/axi_ctl_register_v1_0 @@ -83,15 +83,33 @@ drivers: web: - web/koheron.ts - web/jquery.flot.d.ts - - ./web/fft.ts - - ./web/app.ts - - ./web/control.ts - - ./web/plot.ts - - ./web/temperature-sensor.ts - - ./web/power-monitor.ts - - ./web/precision-adc.ts - - ./web/precision-dac.ts - - ./web/clock-generator.ts - - ./web/index.html - web/main.css - - web/navigation.ts + - web/dds-frequency/dds-frequency.html + - web/dds-frequency/dds-frequency.ts + - ./web/index.html + - ./web/app.ts + - ./web/fft.ts + - ./web/fft/fft-window.html + - ./web/fft/input-channel.html + - ./web/fft/fft-app.ts + - web/plot-basics/plot-basics.ts + - web/plot-basics/plot-basics.html + - ./web/plot/plot.ts + - ./web/plot/yunit.html + - ./web/plot/peak-detection.html + - ./web/precision-channels/precision-adc.ts + - ./web/precision-channels/precision-dac.ts + - ./web/precision-channels/precision-channels-app.ts + - ./web/precision-channels/precision-channels.html + - ./web/clock-generator/clock-generator.ts + - ./web/clock-generator/clock-generator-app.ts + - ./web/clock-generator/sampling-frequency.html + - ./web/clock-generator/reference-clock.html + - ./web/export-file/export-file.html + - ./web/export-file/export-file.ts + - ./web/temperature-sensor/temperature-sensor.html + - ./web/temperature-sensor/temperature-sensor.ts + - ./web/temperature-sensor/temperature-sensor-app.ts + - ./web/power-monitor/power-monitor.html + - ./web/power-monitor/power-monitor.ts + - ./web/power-monitor/power-monitor-app.ts diff --git a/examples/alpha250/fft/web/app.ts b/examples/alpha250/fft/web/app.ts index 7ed16c089..e520d2d3d 100644 --- a/examples/alpha250/fft/web/app.ts +++ b/examples/alpha250/fft/web/app.ts @@ -1,100 +1,69 @@ class App { - public control: Control; + + private imports: Imports; public plot: Plot; + private plotBasics: PlotBasics; private fft: FFT; - private precisionDac: PrecisionDac; - + public fftApp: FFTApp; + public ddsFrequency: DDSFrequency; private temperatureSensor: TemperatureSensor; + private temperatureSensorApp: TemperatureSensorApp; private powerMonitor: PowerMonitor; + private powerMonitorApp: PowerMonitorApp; + private clockGenerator: ClockGenerator; + private clockGeneratorApp: ClockGeneratorApp; + private precisionDac: PrecisionDac; private precisionAdc: PrecisionAdc; - private clkGenerator: ClockGenerator; - - private temperatureVoltageReference: HTMLSpanElement; - private temperatureBoardSpan: HTMLSpanElement; - private temperatureZynqSpan: HTMLSpanElement; - private supplyMainVoltageSpan: HTMLSpanElement; - private supplyMainCurrentSpan: HTMLSpanElement; - private supplyClockVoltageSpan: HTMLSpanElement; - private supplyClockCurrentSpan: HTMLSpanElement; - - private precisionAdcNum: number; - private precisionAdcSpans: HTMLSpanElement[]; + private precisionChannelsApp: PrecisionChannelsApp; + private exportFile: ExportFile; - private navigation: Navigation; + private n_pts: number; + private x_min: number; + private x_max: number; + private y_min: number; + private y_max: number; constructor(window: Window, document: Document, ip: string, plot_placeholder: JQuery) { let sockpoolSize: number = 10; let client = new Client(ip, sockpoolSize); - this.temperatureVoltageReference = document.getElementById('temperature-voltage-reference'); - this.temperatureBoardSpan = document.getElementById('temperature-board'); - this.temperatureZynqSpan = document.getElementById('temperature-zynq'); - this.supplyMainVoltageSpan = document.getElementById('supply-main-voltage'); - this.supplyMainCurrentSpan = document.getElementById('supply-main-current'); - this.supplyClockVoltageSpan = document.getElementById('supply-clock-voltage'); - this.supplyClockCurrentSpan = document.getElementById('supply-clock-current'); - - this.precisionAdcNum = 4; - this.precisionAdcSpans = []; - - for (let i:number = 0; i < this.precisionAdcNum; i++) { - this.precisionAdcSpans[i] = document.getElementById('precision-adc-' + i.toString()); - } - - window.addEventListener('load', () => { + window.addEventListener('HTMLImportsLoaded', () => { client.init( () => { + this.imports = new Imports(document); this.fft = new FFT(client); this.precisionDac = new PrecisionDac(client); + this.precisionAdc = new PrecisionAdc(client); this.temperatureSensor = new TemperatureSensor(client); this.powerMonitor = new PowerMonitor(client); - this.precisionAdc = new PrecisionAdc(client); - this.clkGenerator = new ClockGenerator(client); - this.navigation = new Navigation(document); + this.clockGenerator = new ClockGenerator(client); this.fft.init( () => { - this.control = new Control(document, this.fft, this.precisionDac, this.clkGenerator); - this.plot = new Plot(document, plot_placeholder, this.fft, this.control); - this.updateTemperatures(); - this.updateSupplies(); - this.updatePrecisionAdcValues(); - }); - }); - }, false); + this.fftApp = new FFTApp(document, this.fft); + this.ddsFrequency = new DDSFrequency(document, this.fft); - window.onbeforeunload = () => { client.exit(); }; - } + this.n_pts = this.fft.fft_size / 2; + this.x_min = 0; + this.x_max = this.fft.status.fs / 1E6 / 2; + this.y_min = -200; + this.y_max = 170; - private updateTemperatures() { - this.temperatureSensor.getTemperatures((temperatures: Float32Array) => { - this.temperatureVoltageReference.innerHTML = temperatures[0].toFixed(3).toString(); - this.temperatureBoardSpan.innerHTML = temperatures[1].toFixed(3).toString(); - this.temperatureZynqSpan.innerHTML = temperatures[2].toFixed(3).toString(); + this.plotBasics = new PlotBasics(document, plot_placeholder, this.plot, this.n_pts, this.x_min, this.x_max, this.y_min, this.y_max, this.fft, "", "Frequency (MHz)"); + this.plot = new Plot(document, this.fft, this.plotBasics); - requestAnimationFrame( () => { this.updateTemperatures(); } ); - }); - } + this.temperatureSensorApp = new TemperatureSensorApp(document, this.temperatureSensor); + this.powerMonitorApp = new PowerMonitorApp(document, this.powerMonitor); + this.clockGeneratorApp = new ClockGeneratorApp(document, this.clockGenerator); + this.precisionChannelsApp = new PrecisionChannelsApp(document, this.precisionAdc, this.precisionDac); + this.exportFile = new ExportFile(document, this.plot); - private updateSupplies() { - this.powerMonitor.getSuppliesUI((supplyValues: Float32Array) => { - this.supplyMainCurrentSpan.innerHTML = (supplyValues[0] * 1E3).toFixed(1).toString(); - this.supplyMainVoltageSpan.innerHTML = supplyValues[1].toFixed(3).toString(); - this.supplyClockCurrentSpan.innerHTML = (supplyValues[2] * 1E3).toFixed(1).toString(); - this.supplyClockVoltageSpan.innerHTML = supplyValues[3].toFixed(3).toString(); + }); + }); + }, false); - requestAnimationFrame( () => { this.updateSupplies(); }); - }); + window.onbeforeunload = () => { client.exit(); }; } - private updatePrecisionAdcValues() { - this.precisionAdc.getAdcValues((adcValues: Float32Array) => { - for (let i: number = 0; i < this.precisionAdcNum; i++) { - this.precisionAdcSpans[i].innerHTML = (adcValues[i] * 1000).toFixed(4).toString(); - } - - requestAnimationFrame( () => { this.updatePrecisionAdcValues(); }); - }); - } } let app = new App(window, document, location.hostname, $('#plot-placeholder')); \ No newline at end of file diff --git a/examples/alpha250/fft/web/clock-generator/clock-generator-app.ts b/examples/alpha250/fft/web/clock-generator/clock-generator-app.ts new file mode 100644 index 000000000..3792b4da6 --- /dev/null +++ b/examples/alpha250/fft/web/clock-generator/clock-generator-app.ts @@ -0,0 +1,32 @@ +class ClockGeneratorApp { + + private clkgenInputs: HTMLInputElement[]; + + constructor(document: Document, private driver) { + this.clkgenInputs = document.getElementsByClassName("clkgen-input"); + this.initClkgenInputs(); + this.updateReferenceClock(); + } + + private initClkgenInputs(): void { + for (let i = 0; i < this.clkgenInputs.length; i ++) { + this.clkgenInputs[i].addEventListener('change', (event) => { + this.driver[(event.currentTarget).dataset.command](parseInt((event.currentTarget).value)); + }) + } + } + + private updateReferenceClock() { + this.driver.getReferenceClock( (clkin: number) => { + + let clkIndex: string = "0"; + if (clkin !== 0) { + clkIndex = "2"; + } + + (document.querySelector("[data-command='setReferenceClock'][value='" + clkIndex + "']")).checked = true; + requestAnimationFrame( () => { this.updateReferenceClock(); } ) + }); + } + +} \ No newline at end of file diff --git a/examples/alpha250/fft/web/clock-generator.ts b/examples/alpha250/fft/web/clock-generator/clock-generator.ts similarity index 100% rename from examples/alpha250/fft/web/clock-generator.ts rename to examples/alpha250/fft/web/clock-generator/clock-generator.ts diff --git a/examples/alpha250/fft/web/clock-generator/reference-clock.html b/examples/alpha250/fft/web/clock-generator/reference-clock.html new file mode 100644 index 000000000..ba1249492 --- /dev/null +++ b/examples/alpha250/fft/web/clock-generator/reference-clock.html @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/examples/alpha250/fft/web/clock-generator/sampling-frequency.html b/examples/alpha250/fft/web/clock-generator/sampling-frequency.html new file mode 100644 index 000000000..24c217559 --- /dev/null +++ b/examples/alpha250/fft/web/clock-generator/sampling-frequency.html @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/examples/alpha250/fft/web/control.ts b/examples/alpha250/fft/web/control.ts deleted file mode 100644 index 46a86d918..000000000 --- a/examples/alpha250/fft/web/control.ts +++ /dev/null @@ -1,202 +0,0 @@ -// Control widget -// (c) Koheron - -class Control { - private channelNum: number; - - private precisionDacNum: number; - private precisionDacInputs: HTMLInputElement[]; - private precisionDacSliders: HTMLInputElement[]; - - private frequencies: Array; - private frequencyInputs: HTMLInputElement[]; - private frequencySliders: HTMLInputElement[]; - - public referenceClock: string; - private referenceClockInternalInput: HTMLInputElement; - private referenceClockExternalInput: HTMLInputElement; - - private samplingFrequency: string; - private samplingFrequency200Input: HTMLInputElement; - private samplingFrequency250Input: HTMLInputElement; - - private inputCh0: HTMLInputElement; - private inputCh1: HTMLInputElement; - - public fftWindowIndex: number; - private fftWindowSelect: HTMLSelectElement; - - constructor(document: Document, private fft: FFT, private PrecisionDac: PrecisionDac, private clkGen: ClockGenerator) { - this.channelNum = 2; - - this.frequencyInputs = []; - this.frequencySliders = []; - - for (let i: number = 0; i < this.channelNum; i++) { - this.frequencyInputs[i] = document.getElementById('frequency-input-' + i.toString()); - this.frequencySliders[i] = document.getElementById('frequency-slider-' + i.toString()); - } - - this.frequencies = new Array(this.channelNum); - - this.precisionDacNum = 4; - - this.precisionDacInputs = []; - this.precisionDacSliders = []; - - for (let i: number = 0; i < this.precisionDacNum; i++) { - this.precisionDacInputs[i] = document.getElementById('precision-dac-input-' + i.toString()); - this.precisionDacSliders[i] = document.getElementById('precision-dac-slider-' + i.toString()); - } - - this.referenceClock = 'internal'; - this.referenceClockInternalInput = document.getElementById('reference-clock-internal'); - this.referenceClockExternalInput = document.getElementById('reference-clock-external'); - - this.samplingFrequency = '250 MHz'; - this.samplingFrequency200Input = document.getElementById('sampling-frequency-200'); - this.samplingFrequency250Input = document.getElementById('sampling-frequency-250'); - - this.inputCh0 = document.getElementById('input-ch0'); - this.inputCh1 = document.getElementById('input-ch1'); - - this.fftWindowIndex = 1; - this.fftWindowSelect = document.getElementById("fft-window"); - - this.updateDacValues(); - this.updateReferenceClock(); - this.updateFFTWindowInputs(); - this.updateControls(); - } - - // Updateters - - private updateControls() { - this.fft.getControlParameters( (sts: IFFTStatus) => { - for (let i: number = 0; i < this.channelNum; i++) { - if (document.activeElement !== this.frequencyInputs[i]) { - this.frequencyInputs[i].value = (sts.dds_freq[i] / 1e6).toFixed(6); - } - - if (document.activeElement !== this.frequencySliders[i]) { - this.frequencySliders[i].value = (sts.dds_freq[i] / 1e6).toFixed(6); - this.frequencySliders[i].max = (sts.fs / 1e6 / 2).toFixed(1); - } - - if (sts.fs === 200E6) { - this.samplingFrequency = '200 MHz'; - this.samplingFrequency200Input.checked = true; - this.samplingFrequency250Input.checked = false; - } else { - this.samplingFrequency = '250 MHz'; - this.samplingFrequency200Input.checked = false; - this.samplingFrequency250Input.checked = true; - } - - if (sts.channel === 0) { - this.inputCh0.checked = true; - this.inputCh1.checked = false; - } else { - this.inputCh0.checked = false; - this.inputCh1.checked = true; - } - } - - requestAnimationFrame( () => { this.updateControls(); } ) - }); - } - - private updateDacValues() { - this.PrecisionDac.getDacValues( (dacValues: Float32Array) => { - for (let i: number = 0; i < this.precisionDacNum; i++) { - if (document.activeElement !== this.precisionDacInputs[i]) { - this.precisionDacInputs[i].value = (dacValues[i] * 1000).toFixed(3).toString(); - } - - if (document.activeElement !== this.precisionDacSliders[i]) { - this.precisionDacSliders[i].value = (dacValues[i] * 1000).toFixed(3).toString(); - } - } - - requestAnimationFrame( () => { this.updateDacValues(); } ) - }); - } - - private updateReferenceClock() { - this.clkGen.getReferenceClock( (clkin: number) => { - if (clkin === 0) { - this.referenceClock = 'external'; - this.referenceClockInternalInput.checked = false; - this.referenceClockExternalInput.checked = true; - } else { - this.referenceClock = 'internal'; - this.referenceClockInternalInput.checked = true; - this.referenceClockExternalInput.checked = false; - } - - requestAnimationFrame( () => { this.updateReferenceClock(); } ) - }); - } - - private updateFFTWindowInputs() { - this.fft.getFFTWindowIndex( (windowIndex: number) => { - this.fftWindowIndex = windowIndex; - this.fftWindowSelect.value = windowIndex.toString(); - requestAnimationFrame( () => { this.updateFFTWindowInputs(); } ) - }); - } - - // Setters - - setFrequency(channel: number, input: HTMLInputElement) { - let frequencyValue = input.value; - - if (input.type === 'number') { - this.frequencySliders[channel].value = frequencyValue; - } else if (input.type === 'range') { - this.frequencyInputs[channel].value = frequencyValue; - } - - this.fft.setDDSFreq(channel, 1e6 * parseFloat(frequencyValue)); - } - - setPrecisionDac(channel: number, input: HTMLInputElement) { - let precisionDacValue = input.value; - - if (input.type === 'number') { - this.precisionDacSliders[channel].value = precisionDacValue; - } else if (input.type === 'range') { - this.precisionDacInputs[channel].value = precisionDacValue; - } - - this.PrecisionDac.setDac(channel, parseFloat(precisionDacValue) / 1000); - } - - setReferenceClock(referenceClock: string) { - this.referenceClock = referenceClock; - - if (this.referenceClock === 'external') { - this.clkGen.setReferenceClock(0); - } else { - this.clkGen.setReferenceClock(2); - } - } - - setSamplingFrequency(samplingFrequency: string) { - this.samplingFrequency = samplingFrequency; - - if (this.samplingFrequency === '200 MHz') { - this.clkGen.setSamplingFrequency(0); - } else { // 250 MHz - this.clkGen.setSamplingFrequency(1); - } - } - - setInputChannel(channel: number) { - this.fft.setInputChannel(channel); - } - - setFFTWindow(windowIndex: number) { - this.fft.setFFTWindow(windowIndex); - } -} \ No newline at end of file diff --git a/examples/alpha250/fft/web/export-file/export-file.html b/examples/alpha250/fft/web/export-file/export-file.html new file mode 100644 index 000000000..a7a10e011 --- /dev/null +++ b/examples/alpha250/fft/web/export-file/export-file.html @@ -0,0 +1,13 @@ + \ No newline at end of file diff --git a/examples/alpha250/fft/web/export-file/export-file.ts b/examples/alpha250/fft/web/export-file/export-file.ts new file mode 100644 index 000000000..3c3dfcc47 --- /dev/null +++ b/examples/alpha250/fft/web/export-file/export-file.ts @@ -0,0 +1,72 @@ +// Export file widget +// (c) Koheron + +class ExportFile { + + private exportDataButtons: HTMLButtonElement[]; + private exportPlotButtons: HTMLButtonElement[]; + + constructor(document: Document, private plot_) { + this.exportDataButtons = document.getElementsByClassName("export-data"); + this.exportPlotButtons = document.getElementsByClassName("export-plot"); + this.initExportData(); + this.initExportPlot(); + } + + initExportData(): void { + for (let i = 0; i < this.exportDataButtons.length; i++) { + this.exportDataButtons[i].addEventListener('click', (event) => { + + let csvContent = "data:text/csv;charset=utf-8,"; + let dateTime = new Date(); + let referenceClock: string = (document.querySelector("[data-command='setReferenceClock']:checked")).dataset.valuestr; + let fftWindowIndex: string = (document.querySelector("[data-command='setFFTWindow']")).value; + let inputChannel: string = (document.querySelector("[name='input-channel']:checked")).value; + let samplingFrequency: string = (document.querySelector("[name='sampling-frequency']:checked")).dataset.valuestr; + let ddsInputs = document.querySelectorAll(".dds-channel-input[type='range']"); + + csvContent += "Koheron Alpha \n"; + csvContent += dateTime.getDate() + "/" + (dateTime.getMonth()+1) + "/" + dateTime.getFullYear() + " " ; + csvContent += dateTime.getHours() + ":" + dateTime.getMinutes() + ":" + dateTime.getSeconds() + "\n"; + csvContent += "\n"; + csvContent += '"Window",' + fftWindowIndex + "\n"; + csvContent += '"Input channel",' + inputChannel + "\n"; + csvContent += '"Sampling frequency (MHz)",' + samplingFrequency + "\n"; + csvContent += '"Reference clock (10 MHz)",' + referenceClock + "\n"; + for (let i: number = 0; i < ddsInputs.length; i++) { + let channel: string = ddsInputs[i].dataset.channel; + csvContent += '"Channel ' + channel + ' DDS frequency (MHz)",' + ddsInputs[i].value + "\n"; + } + + csvContent += "\n\n"; + + let yUnit: string = (document.querySelector(".unit-input:checked")).value; + csvContent += '"Frequency (MHz)","' + this.plot_.yLabel + ' (' + yUnit.replace("-", "/") + ')" \n'; + + this.plot_.plot_data.forEach( (rowArray) => { + let row = rowArray.join(","); + csvContent += row + "\n"; + }); + + let exportGroup = this.exportDataButtons[i].parentElement; + let exportLink = (exportGroup.getElementsByTagName("a")[0]); + exportLink.href = encodeURI(csvContent); + exportLink.click(); + }) + } + } + + initExportPlot(): void { + for (let i = 0; i < this.exportPlotButtons.length; i++) { + this.exportPlotButtons[i].addEventListener('click', (event) => { + let canvas = this.plot_.plotBasics.plot.getCanvas(); + let imagePng = canvas.toDataURL("image/png").replace("image/png", "image/octet-stream"); + let exportGroup = this.exportPlotButtons[i].parentElement; + let exportLink = (exportGroup.getElementsByTagName("a")[0]); + exportLink.href = imagePng; + exportLink.click(); + }) + } + } + +} diff --git a/examples/alpha250/fft/web/fft/fft-app.ts b/examples/alpha250/fft/web/fft/fft-app.ts new file mode 100644 index 000000000..8ab388a1c --- /dev/null +++ b/examples/alpha250/fft/web/fft/fft-app.ts @@ -0,0 +1,78 @@ +// FFT widget +// (c) Koheron + +class FFTApp { + private channelNum: number = 2; + private fftSelects: HTMLSelectElement[]; + private fftInputs: HTMLInputElement[]; + + constructor(document: Document, private driver) { + this.fftSelects = document.getElementsByClassName("fft-select"); + this.initFFTSelects(); + this.fftInputs = document.getElementsByClassName("fft-input"); + this.initFFTInputs(); + + this.updateFFTWindowInputs(); + this.updateControls(); + } + + // Updaters + + private updateControls() { + this.driver.getControlParameters( (sts: IFFTStatus) => { + + for (let i = 0; i < this.channelNum; i++) { + let inputs = document.querySelectorAll(".dds-channel-input[data-command='setDDSFreq'][data-channel='" + i.toString() + "']"); + let inputsArray = []; + for (let j = 0; j < inputs.length; j++) { + inputsArray.push(inputs[j]); + } + + if (inputsArray.indexOf(document.activeElement) == -1) { + for (let j = 0; j < inputs.length; j++) { + inputs[j].value = (sts.dds_freq[i] / 1e6).toFixed(6); + if (inputs[j].type == "range") { + inputs[j].max = (sts.fs / 1e6 / 2).toFixed(1); + } + } + } + } + + if (sts.fs === 200E6) { + (document.querySelector("[data-command='setSamplingFrequency'][value='0']")).checked = true; + } else { + (document.querySelector("[data-command='setSamplingFrequency'][value='1']")).checked = true; + } + + (document.querySelector("[data-command='setInputChannel'][value='" + sts.channel.toString() + "']")).checked = true; + + requestAnimationFrame( () => { this.updateControls(); } ) + }); + } + + private updateFFTWindowInputs() { + this.driver.getFFTWindowIndex( (windowIndex: number) => { + (document.querySelector("[data-command='setFFTWindow']")).value = windowIndex.toString(); + requestAnimationFrame( () => { this.updateFFTWindowInputs(); } ) + }); + } + + // Setters + + initFFTSelects(): void { + for (let i = 0; i < this.fftSelects.length; i++) { + this.fftSelects[i].addEventListener('change', (event) => { + this.driver[(event.currentTarget).dataset.command]((event.currentTarget).value); + }) + } + } + + initFFTInputs(): void { + for (let i = 0; i < this.fftInputs.length; i++) { + this.fftInputs[i].addEventListener('change', (event) => { + this.driver[(event.currentTarget).dataset.command]((event.currentTarget).value); + }) + } + } + +} \ No newline at end of file diff --git a/examples/alpha250/fft/web/fft/fft-window.html b/examples/alpha250/fft/web/fft/fft-window.html new file mode 100644 index 000000000..488f62ea7 --- /dev/null +++ b/examples/alpha250/fft/web/fft/fft-window.html @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/examples/alpha250/fft/web/fft/input-channel.html b/examples/alpha250/fft/web/fft/input-channel.html new file mode 100644 index 000000000..cde6ef153 --- /dev/null +++ b/examples/alpha250/fft/web/fft/input-channel.html @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/examples/alpha250/fft/web/functions.ts b/examples/alpha250/fft/web/functions.ts deleted file mode 100644 index 626a6d569..000000000 --- a/examples/alpha250/fft/web/functions.ts +++ /dev/null @@ -1,69 +0,0 @@ -class Functions { - private isFunctions: boolean = false; - private functionsSpan: HTMLSpanElement; - - constructor(document: Document) { - this.functionsSpan = document.getElementById("functions"); - } - - updateFunctionsSpan(event) { - let rect = event.getBoundingClientRect(); - - this.functionsSpan.style.left = (rect.x).toString() + "px"; - this.functionsSpan.style.top = (rect.y + rect.height + 10).toString() + "px"; - this.functionsSpan.style.zIndex = "1"; - this.functionsSpan.style.display = "inline-block"; - } - - activateFunctions() { - if (this.isFunctions) { - this.isFunctions = false; - this.hideFunctions(); - } else { - this.isFunctions = true; - } - } - - displayFunctions(event) { - let pythonFunction: string = ""; - let pythonClass: string = ""; - - if (this.isFunctions) { - if (event.id === "input-channel-label") { - pythonFunction = "set_input_channel(self, channel)"; - pythonClass = "FFT"; - } else if (event.id === "reference-clock-label") { - pythonFunction = "set_reference_clock(self, clkin)"; - pythonClass = "Alpha"; - } else if (event.id === "sampling-frequency-label") { - pythonFunction = "set_sampling_frequency(self, fs_select)"; - pythonClass = "Alpha"; - } else if (event.id === "dds-frequency-span") { - pythonFunction = "set_dds_freq(self, channel, freq)"; - pythonClass = "FFT"; - } else if (event.id === "precision-dacs-span") { - pythonFunction = "set_precision_dac_volts(self, channel, voltage)"; - pythonClass = "Alpha"; - } else if (event.id === "precision-adcs-span") { - pythonFunction = "get_precision_adc_values(self)"; - pythonClass = "Alpha"; - } else if (event.id === "temperature-span") { - pythonFunction = "get_temperatures(self)"; - pythonClass = "Alpha"; - } else if (event.id === "fft-window-label") { - pythonFunction = "set_fft_window(self, window_name)"; - pythonClass = "FFT"; - } else if (event.id === "psd-label") { - pythonFunction = "read_psd(self)"; - pythonClass = "FFT"; - } - - this.functionsSpan.innerHTML = pythonFunction + ", class: " + pythonClass; - this.updateFunctionsSpan(event); - } - } - - hideFunctions() { - this.functionsSpan.style.display = "none"; - } -} \ No newline at end of file diff --git a/examples/alpha250/fft/web/index.html b/examples/alpha250/fft/web/index.html index a1e889d77..3e0fc9a28 100644 --- a/examples/alpha250/fft/web/index.html +++ b/examples/alpha250/fft/web/index.html @@ -16,362 +16,69 @@ + + + - - - - +
-
-
Frequency (MHz)
- - - - - +
-
- - - -
- - - -
- dBm / Hz -
- -
- dBm -
- -
- nV / rtHz -
- -
- - - -
- - - - - -
- - - -
- - - -
- 0 -
- -
- 1 -
- -
- - - -
- - - -
- Internal -
- -
- External -
- -
- - - - -
- - - -
- 200 MHz -
- -
- 250 MHz -
- -
- - -
- - -
- -
- - - - - - - -
- -
- - DDS Frequency (MHz) - -
- - - - - - - - - - - - -
- OUT 0 - - - - -
- OUT 1 - - - - -
- -
- -
- -
- File -
- -
- - - - - - - - - - -
- +
+
+
+
+
+
+
+
+
- -
-
- -
- -
- -
- - Precision DACs & ADCs (mV) - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- DAC 0 - - - - - ADC 0
- DAC 1 - - - - - ADC 1
- DAC 2 - - - - - ADC 2
- DAC 3 - - - - - ADC 3
- -
-
- +
-
-
-
- -
- - Temperature (°C) -
- - - - - - - - - - - - - - -
Voltage Reference
Board
Zynq
- -
-
+
-
-
-
- -
- Supply -
- - - - - - - - - - - - - - - - - -
MainClock
Volt. (V)
Cur. (mA)
- -
-
- +
-
diff --git a/examples/alpha250/fft/web/navigation.ts b/examples/alpha250/fft/web/navigation.ts deleted file mode 100644 index cc4d6c1ee..000000000 --- a/examples/alpha250/fft/web/navigation.ts +++ /dev/null @@ -1,37 +0,0 @@ -class Navigation { - private navigationDiv: HTMLDivElement; - private navigationLinksDiv: HTMLDivElement; - private collapseBtn: HTMLButtonElement; - private mainDiv: HTMLDivElement; - - constructor(document: Document) { - this.navigationDiv = document.getElementById("navigation"); - this.navigationLinksDiv = document.getElementById("navigation-links"); - this.collapseBtn = document.getElementById("collapse-btn"); - this.mainDiv = document.getElementById("main"); - } - - openNavigation(): void { - this.navigationDiv.style.width = "100px"; - this.mainDiv.style.marginLeft = "100px"; - this.collapseBtn.innerHTML = "<"; - this.collapseBtn.value = "close"; - this.navigationLinksDiv.style.display = "block"; - } - - closeNavigation(): void { - this.navigationDiv.style.width = "30px"; - this.mainDiv.style.marginLeft = "30px"; - this.collapseBtn.innerHTML = ">"; - this.collapseBtn.value = "open"; - this.navigationLinksDiv.style.display = "none"; - } - - collapseBtnClick(event): void { - if (event.value === 'close') { - this.closeNavigation(); - } else if (event.value === 'open') { - this.openNavigation(); - } - } -} \ No newline at end of file diff --git a/examples/alpha250/fft/web/plot/peak-detection.html b/examples/alpha250/fft/web/plot/peak-detection.html new file mode 100644 index 000000000..5b9695f1d --- /dev/null +++ b/examples/alpha250/fft/web/plot/peak-detection.html @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/examples/alpha250/fft/web/plot/plot.ts b/examples/alpha250/fft/web/plot/plot.ts new file mode 100644 index 000000000..25ddeda3f --- /dev/null +++ b/examples/alpha250/fft/web/plot/plot.ts @@ -0,0 +1,56 @@ +// Plot widget +// (c) Koheron + +class Plot { + public n_pts: number; + public plot: jquery.flot.plot; + public plot_data: Array>; + + public yLabel: string = "Power Spectral Density"; + private peakDatapoint: number[]; + + public x_max: number; + + constructor(document: Document, private fft: FFT, private plotBasics: PlotBasics) { + + this.n_pts = this.fft.fft_size / 2; + this.x_max = this.fft.status.fs / 1E6 / 2; + + this.peakDatapoint = []; + this.plot_data = []; + + this.updatePlot(this.x_max); + } + + updatePlot(max_x: number) { + this.fft.read_psd( (psd: Float32Array) => { + let yUnit: string = (document.querySelector(".unit-input:checked")).value; + this.peakDatapoint = [ max_x / this.n_pts , this.convertValue(psd[0], yUnit)]; + + for (let i: number = 0; i <= this.n_pts; i++) { + let freq: number = (i + 1) * max_x / this.n_pts; // MHz + let convertedPsd: number = this.convertValue(psd[i], yUnit); + this.plot_data[i] = [freq, convertedPsd]; + }; + + this.plotBasics.redraw(this.plot_data, this.n_pts, this.peakDatapoint, this.yLabel, () => { + requestAnimationFrame( () => { this.updatePlot(max_x); } ); + }); + }); + } + + convertValue(inValue: number, outUnit: string): number { + // inValue in W / Hz + let outValue: number = 0; + + if (outUnit === "dBm-Hz") { + outValue = 10 * Math.log(inValue / 1E-3) / Math.LN10; + } else if (outUnit === "dBm") { + outValue = 10 * Math.log(inValue * (this.fft.status.W2 / this.fft.status.W1) * this.fft.status.fs / this.fft.fft_size / 1E-3) / Math.LN10; + } else if (outUnit === "nv-rtHz") { + outValue = Math.sqrt(50 * inValue) * 1E9; + } + + return outValue; + } +} diff --git a/examples/alpha250/fft/web/plot/yunit.html b/examples/alpha250/fft/web/plot/yunit.html new file mode 100644 index 000000000..d19805eec --- /dev/null +++ b/examples/alpha250/fft/web/plot/yunit.html @@ -0,0 +1,12 @@ + \ No newline at end of file diff --git a/examples/alpha250/fft/web/power-monitor/power-monitor-app.ts b/examples/alpha250/fft/web/power-monitor/power-monitor-app.ts new file mode 100644 index 000000000..eb49d31f6 --- /dev/null +++ b/examples/alpha250/fft/web/power-monitor/power-monitor-app.ts @@ -0,0 +1,24 @@ +class PowerMonitorApp { + + private supplySpans: HTMLSpanElement[]; + + constructor(document: Document, private powerMonitor: PowerMonitor) { + this.supplySpans = document.getElementsByClassName("supply-span"); + this.updateSupplies(); + } + + private updateSupplies() { + this.powerMonitor.getSuppliesUI((supplyValues: Float32Array) => { + for (let i = 0; i < this.supplySpans.length; i ++) { + let value: string = ""; + if (this.supplySpans[i].dataset.type === "voltage") { + value = supplyValues[parseInt(this.supplySpans[i].dataset.index)].toFixed(3); + } else if (this.supplySpans[i].dataset.type === "current") { + value = (supplyValues[parseInt(this.supplySpans[i].dataset.index)] * 1E3).toFixed(1); + } + this.supplySpans[i].textContent = value; + } + requestAnimationFrame( () => { this.updateSupplies(); }); + }); + } +} \ No newline at end of file diff --git a/examples/alpha250/fft/web/power-monitor/power-monitor.html b/examples/alpha250/fft/web/power-monitor/power-monitor.html new file mode 100644 index 000000000..53844ec4f --- /dev/null +++ b/examples/alpha250/fft/web/power-monitor/power-monitor.html @@ -0,0 +1,20 @@ + \ No newline at end of file diff --git a/examples/alpha250/fft/web/power-monitor.ts b/examples/alpha250/fft/web/power-monitor/power-monitor.ts similarity index 100% rename from examples/alpha250/fft/web/power-monitor.ts rename to examples/alpha250/fft/web/power-monitor/power-monitor.ts diff --git a/examples/alpha250/fft/web/precision-adc.ts b/examples/alpha250/fft/web/precision-channels/precision-adc.ts similarity index 100% rename from examples/alpha250/fft/web/precision-adc.ts rename to examples/alpha250/fft/web/precision-channels/precision-adc.ts diff --git a/examples/alpha250/fft/web/precision-channels/precision-channels-app.ts b/examples/alpha250/fft/web/precision-channels/precision-channels-app.ts new file mode 100644 index 000000000..5726832f6 --- /dev/null +++ b/examples/alpha250/fft/web/precision-channels/precision-channels-app.ts @@ -0,0 +1,60 @@ +class PrecisionChannelsApp { + + private precisionChannelsNum: number = 4; + private precisionDacInputs: HTMLInputElement[]; + + constructor(document: Document, private precisionAdc: PrecisionAdc, private precisionDac: PrecisionDac) { + this.precisionDacInputs = document.getElementsByClassName("precision-dac-input"); + this.updatePrecisionAdc(); + this.updatePrecisionDac(); + this.initPrecisionDacInputs(); + } + + private updatePrecisionAdc() { + this.precisionAdc.getAdcValues((adcValues: Float32Array) => { + for (let i: number = 0; i < this.precisionChannelsNum; i++) { + (document.querySelector(".precision-adc-span[data-channel='" + i.toString() + "']")).textContent = (adcValues[i] * 1000).toFixed(4); + } + requestAnimationFrame( () => { this.updatePrecisionAdc(); }); + }); + } + + private updatePrecisionDac() { + this.precisionDac.getDacValues( (dacValues: Float32Array) => { + for (let i = 0; i < this.precisionChannelsNum; i++) { + let inputs = document.querySelectorAll(".precision-dac-input[data-command='setDac'][data-channel='" + i.toString() + "']"); + let inputsArray = []; + for (let j = 0; j < inputs.length; j++) { + inputsArray.push(inputs[j]); + } + + if (inputsArray.indexOf(document.activeElement) == -1) { + for (let j = 0; j < inputs.length; j++) { + inputs[j].value = (dacValues[i] * 1000).toFixed(3).toString(); + } + } + } + + requestAnimationFrame( () => { this.updatePrecisionDac(); } ) + }); + } + + initPrecisionDacInputs(): void { + let events = ['change', 'input']; + for (let j = 0; j < events.length; j++) { + for (let i = 0; i < this.precisionDacInputs.length; i++) { + this.precisionDacInputs[i].addEventListener(events[j], (event) => { + let counterType: string = "number"; + if ((event.currentTarget).type == "number") { + counterType = "range"; + } + let command = (event.currentTarget).dataset.command; + let channel = (event.currentTarget).dataset.channel; + let value = (event.currentTarget).value; + (document.querySelector("[data-command='" + command + "'][data-channel='" + channel +"'][type='" + counterType + "']")).value = value ; + this.precisionDac[command](channel, parseFloat(value) / 1000); + }) + } + } + } +} diff --git a/examples/alpha250/fft/web/precision-channels/precision-channels.html b/examples/alpha250/fft/web/precision-channels/precision-channels.html new file mode 100644 index 000000000..237035239 --- /dev/null +++ b/examples/alpha250/fft/web/precision-channels/precision-channels.html @@ -0,0 +1,66 @@ + \ No newline at end of file diff --git a/examples/alpha250/fft/web/precision-dac.ts b/examples/alpha250/fft/web/precision-channels/precision-dac.ts similarity index 100% rename from examples/alpha250/fft/web/precision-dac.ts rename to examples/alpha250/fft/web/precision-channels/precision-dac.ts diff --git a/examples/alpha250/fft/web/temperature-sensor/temperature-sensor-app.ts b/examples/alpha250/fft/web/temperature-sensor/temperature-sensor-app.ts new file mode 100644 index 000000000..db5429e40 --- /dev/null +++ b/examples/alpha250/fft/web/temperature-sensor/temperature-sensor-app.ts @@ -0,0 +1,18 @@ +class TemperatureSensorApp { + + private temperatureSpans: HTMLSpanElement[]; + + constructor(document: Document, private temperatureSensor: TemperatureSensor) { + this.temperatureSpans = document.getElementsByClassName("temperature-span"); + this.updateTemperatures(); + } + + private updateTemperatures() { + this.temperatureSensor.getTemperatures((temperatures: Float32Array) => { + for (let i = 0; i < this.temperatureSpans.length; i ++) { + this.temperatureSpans[i].textContent = temperatures[parseInt(this.temperatureSpans[i].dataset.index)].toFixed(3); + } + requestAnimationFrame( () => { this.updateTemperatures(); } ); + }); + } +} diff --git a/examples/alpha250/fft/web/temperature-sensor/temperature-sensor.html b/examples/alpha250/fft/web/temperature-sensor/temperature-sensor.html new file mode 100644 index 000000000..6f056b08c --- /dev/null +++ b/examples/alpha250/fft/web/temperature-sensor/temperature-sensor.html @@ -0,0 +1,24 @@ + \ No newline at end of file diff --git a/examples/alpha250/fft/web/temperature-sensor.ts b/examples/alpha250/fft/web/temperature-sensor/temperature-sensor.ts similarity index 80% rename from examples/alpha250/fft/web/temperature-sensor.ts rename to examples/alpha250/fft/web/temperature-sensor/temperature-sensor.ts index f60aa633b..3f6acacf8 100644 --- a/examples/alpha250/fft/web/temperature-sensor.ts +++ b/examples/alpha250/fft/web/temperature-sensor/temperature-sensor.ts @@ -10,7 +10,6 @@ class TemperatureSensor { } getTemperatures(cb: (temperatures: Float32Array) => void): void { - this.client.readFloat32Array(Command(this.id, this.cmds['get_temperatures']), - (temperatures: Float32Array) => {cb(temperatures)}); + this.client.readFloat32Array(Command(this.id, this.cmds['get_temperatures']), (temperatures: Float32Array) => {cb(temperatures)}); } } \ No newline at end of file diff --git a/examples/alpha250/loopback/config.yml b/examples/alpha250/loopback/config.yml index ed3267bb8..69e05ff47 100644 --- a/examples/alpha250/loopback/config.yml +++ b/examples/alpha250/loopback/config.yml @@ -1,7 +1,7 @@ --- name: loopback board: boards/alpha250 -version: 0.1.0 +version: 0.1.1 cores: - fpga/cores/axi_ctl_register_v1_0 @@ -57,4 +57,4 @@ drivers: web: - web/index.html - web/main.css - - web/navigation.ts + - web/koheron.ts \ No newline at end of file diff --git a/examples/alpha250/phase-noise-analyzer/config.yml b/examples/alpha250/phase-noise-analyzer/config.yml index 143f16700..19db23a94 100644 --- a/examples/alpha250/phase-noise-analyzer/config.yml +++ b/examples/alpha250/phase-noise-analyzer/config.yml @@ -1,6 +1,7 @@ --- name: phase-noise-analyzer board: boards/alpha250 +version: 0.1.0 cores: - fpga/cores/axi_ctl_register_v1_0 @@ -83,5 +84,4 @@ drivers: web: - web/index.html - web/main.css - - web/koheron.ts - - web/navigation.ts + - web/koheron.ts \ No newline at end of file diff --git a/examples/microzed/default/config.yml b/examples/microzed/default/config.yml index 4ff6a5e16..fc0c6752e 100644 --- a/examples/microzed/default/config.yml +++ b/examples/microzed/default/config.yml @@ -39,5 +39,4 @@ web: - web/koheron.ts - ./web/monitor.ts - ./web/app.ts - - web/main.css - - web/navigation.ts + - web/main.css \ No newline at end of file diff --git a/examples/microzed/default/web/app.ts b/examples/microzed/default/web/app.ts index 7ae8a8ee6..10fe88ec4 100644 --- a/examples/microzed/default/web/app.ts +++ b/examples/microzed/default/web/app.ts @@ -2,7 +2,6 @@ class App { private monitor: Monitor; private temperatureSpan: HTMLSpanElement; - private navigation: Navigation; constructor(window: Window, document: Document, ip: string) { let client = new Client(ip, 5); @@ -11,7 +10,6 @@ class App { window.addEventListener('load', () => { client.init( () => { this.monitor = new Monitor(client); - this.navigation = new Navigation(document); this.update(); }); }, false); diff --git a/examples/microzed/default/web/index.html b/examples/microzed/default/web/index.html index 0afc3c929..32bbf60b4 100644 --- a/examples/microzed/default/web/index.html +++ b/examples/microzed/default/web/index.html @@ -21,22 +21,11 @@ - -
Temperature (°C): +

This instrument was built with Koheron Software Development Kit. See settings.
diff --git a/examples/mydc7z015/default/config.yml b/examples/mydc7z015/default/config.yml index 79b9ea5fc..7141d9052 100644 --- a/examples/mydc7z015/default/config.yml +++ b/examples/mydc7z015/default/config.yml @@ -39,5 +39,4 @@ web: - web/koheron.ts - ./web/monitor.ts - ./web/app.ts - - web/main.css - - web/navigation.ts + - web/main.css \ No newline at end of file diff --git a/examples/mydc7z015/default/web/app.ts b/examples/mydc7z015/default/web/app.ts index 8a53ee982..10fe88ec4 100644 --- a/examples/mydc7z015/default/web/app.ts +++ b/examples/mydc7z015/default/web/app.ts @@ -1,8 +1,6 @@ class App { private monitor: Monitor; - private navigation: Navigation; - private temperatureSpan: HTMLSpanElement; constructor(window: Window, document: Document, ip: string) { @@ -12,7 +10,6 @@ class App { window.addEventListener('load', () => { client.init( () => { this.monitor = new Monitor(client); - this.navigation = new Navigation(document); this.update(); }); }, false); diff --git a/examples/mydc7z015/default/web/index.html b/examples/mydc7z015/default/web/index.html index 0c9e51c72..e294c6f89 100644 --- a/examples/mydc7z015/default/web/index.html +++ b/examples/mydc7z015/default/web/index.html @@ -21,25 +21,13 @@ - -
Temperature (°C): +

This instrument was built with Koheron Software Development Kit. See settings.
- diff --git a/examples/red-pitaya/adc-dac-bram/config.yml b/examples/red-pitaya/adc-dac-bram/config.yml index 10c7560fd..94b0b0b35 100644 --- a/examples/red-pitaya/adc-dac-bram/config.yml +++ b/examples/red-pitaya/adc-dac-bram/config.yml @@ -51,4 +51,4 @@ drivers: web: - web/index.html - web/main.css - - web/navigation.ts + - web/koheron.ts \ No newline at end of file diff --git a/examples/red-pitaya/adc-dac/config.yml b/examples/red-pitaya/adc-dac/config.yml index 656c1a6e9..5b1a76f02 100644 --- a/examples/red-pitaya/adc-dac/config.yml +++ b/examples/red-pitaya/adc-dac/config.yml @@ -42,4 +42,4 @@ drivers: web: - web/index.html - web/main.css - - web/navigation.ts \ No newline at end of file + - web/koheron.ts \ No newline at end of file diff --git a/examples/red-pitaya/cluster/config.yml b/examples/red-pitaya/cluster/config.yml index 2716fb6a5..e8f82c6f3 100644 --- a/examples/red-pitaya/cluster/config.yml +++ b/examples/red-pitaya/cluster/config.yml @@ -1,7 +1,7 @@ --- name: cluster board: boards/red-pitaya -version: 0.1.0 +version: 0.1.1 cores: - fpga/cores/axi_ctl_register_v1_0 @@ -58,4 +58,4 @@ drivers: web: - web/index.html - web/main.css - - web/navigation.ts \ No newline at end of file + - web/koheron.ts \ No newline at end of file diff --git a/examples/red-pitaya/decimator/config.yml b/examples/red-pitaya/decimator/config.yml index dc427a95e..aa3e9d4be 100644 --- a/examples/red-pitaya/decimator/config.yml +++ b/examples/red-pitaya/decimator/config.yml @@ -1,7 +1,7 @@ --- name: decimator board: boards/red-pitaya -version: 0.1.0 +version: 0.1.1 cores: - fpga/cores/redp_adc_v1_0 @@ -50,4 +50,4 @@ drivers: web: - web/index.html - web/main.css - - web/navigation.ts \ No newline at end of file + - web/koheron.ts \ No newline at end of file diff --git a/examples/red-pitaya/dual-dds/config.yml b/examples/red-pitaya/dual-dds/config.yml index afd61b6e2..59b953d54 100644 --- a/examples/red-pitaya/dual-dds/config.yml +++ b/examples/red-pitaya/dual-dds/config.yml @@ -47,4 +47,5 @@ web: - ./web/control.ts - ./web/index.html - web/main.css - - web/navigation.ts \ No newline at end of file + - web/dds-frequency/dds-frequency.html + - web/dds-frequency/dds-frequency.ts \ No newline at end of file diff --git a/examples/red-pitaya/dual-dds/web/app.ts b/examples/red-pitaya/dual-dds/web/app.ts index bfe2496cf..4f6e0add3 100644 --- a/examples/red-pitaya/dual-dds/web/app.ts +++ b/examples/red-pitaya/dual-dds/web/app.ts @@ -1,17 +1,19 @@ class App { public control: Control; private driver: DualDDS; - private navigation: Navigation; + private imports: Imports; + private ddsFrequency: DDSFrequency; constructor(window: Window, document: Document, ip: string, plot_placeholder: JQuery) { let client = new Client(ip, 5); - window.addEventListener('load', () => { + window.addEventListener('HTMLImportsLoaded', () => { client.init( () => { + this.imports = new Imports(document); this.driver = new DualDDS(client); this.control = new Control(document, this.driver); - this.navigation = new Navigation(document); + this.ddsFrequency = new DDSFrequency(document, this.driver); }); }, false); diff --git a/examples/red-pitaya/dual-dds/web/control.ts b/examples/red-pitaya/dual-dds/web/control.ts index 70e0df1cb..a164b3b65 100644 --- a/examples/red-pitaya/dual-dds/web/control.ts +++ b/examples/red-pitaya/dual-dds/web/control.ts @@ -1,46 +1,26 @@ // Control widget // (c) Koheron -class Control { - private frequencyInputs: HTMLInputElement[]; - private frequencySliders: HTMLInputElement[]; +interface DualDDSStatus { + dds_freq: number[]; +} - constructor(document: Document, private driver: DualDDS) { - this.frequencyInputs = []; - this.frequencySliders = []; - for (let channel: number = 0; channel < 2; channel++) { - this.frequencyInputs[channel] = document.getElementById('frequency-input-' + channel.toString()); - this.frequencySliders[channel] = document.getElementById('frequency-slider-'+ channel.toString()); - } +class Control { + constructor(document: Document, private driver) { this.update(); } update() { - this.driver.getControlParameters( (sts: DualDDSStatus) => { - for (let channel: number = 0; channel < 2; channel++) { - if (document.activeElement !== this.frequencyInputs[channel]) { - this.frequencyInputs[channel].value = (sts.dds_freq[channel] / 1e6).toFixed(6); - } - if (document.activeElement !== this.frequencySliders[channel]) { - this.frequencySliders[channel].value = (sts.dds_freq[channel]/1e6).toFixed(8); + this.driver.getControlParameters( (status: DualDDSStatus) => { + for (let i: number = 0; i < status.dds_freq.length; i++) { + let inputs = document.querySelectorAll(".dds-channel-input[data-channel='" + i.toString() + "']"); + for (let j: number = 0; j < inputs.length; j++) { + if (document.activeElement !== inputs[j]) { + (inputs[j]).value = (status.dds_freq[i] / 1e6).toFixed(6); + } } } requestAnimationFrame( () => { this.update(); } ) }); } - - - setFrequency(channel: number, event) { - - let frequencyValue = event.value; - - if (event.type === 'number') { - this.frequencySliders[channel].value = frequencyValue; - } else if (event.type === 'range') { - this.frequencyInputs[channel].value = frequencyValue; - } - - this.driver.setDDSFreq(channel, 1e6 * Math.min(parseFloat(frequencyValue), 62.5)); - } - } \ No newline at end of file diff --git a/examples/red-pitaya/dual-dds/web/index.html b/examples/red-pitaya/dual-dds/web/index.html index cac57467c..5af0fe930 100644 --- a/examples/red-pitaya/dual-dds/web/index.html +++ b/examples/red-pitaya/dual-dds/web/index.html @@ -12,6 +12,8 @@ + + @@ -19,62 +21,21 @@ - - - - +
- -
- -
- - DDS Frequency (MHz) - -
- - - - - - - - - - - - -
Channel 0 - - - -
Channel 1 - - - -
- -
- +
- + \ No newline at end of file diff --git a/examples/red-pitaya/fft/config.yml b/examples/red-pitaya/fft/config.yml index b3c383181..c1c32d2d4 100644 --- a/examples/red-pitaya/fft/config.yml +++ b/examples/red-pitaya/fft/config.yml @@ -1,7 +1,7 @@ --- name: fft board: boards/red-pitaya -version: 0.1.0 +version: 0.1.1 cores: - fpga/cores/axi_ctl_register_v1_0 @@ -87,10 +87,20 @@ web: - web/koheron.ts - web/jquery.flot.d.ts - web/laser.ts - - ./web/fft.ts + - web/laser-control.html + - web/dds-frequency/dds-frequency.ts + - web/dds-frequency/dds-frequency.html - ./web/app.ts - - ./web/control.ts - - ./web/plot.ts + - ./web/fft.ts + - ./web/plot/plot.ts + - web/plot-basics/plot-basics.ts + - web/plot-basics/plot-basics.html + - ./web/plot/yunit.html + - ./web/plot/peak-detection.html + - ./web/fft/fft-app.ts + - ./web/fft/fft-window.html + - ./web/fft/input-channel.html + - ./web/export-file/export-file.html + - ./web/export-file/export-file.ts - ./web/index.html - - web/main.css - - web/navigation.ts + - web/main.css \ No newline at end of file diff --git a/examples/red-pitaya/fft/drivers/fft.hpp b/examples/red-pitaya/fft/drivers/fft.hpp index aaff424d3..5f4663175 100644 --- a/examples/red-pitaya/fft/drivers/fft.hpp +++ b/examples/red-pitaya/fft/drivers/fft.hpp @@ -28,7 +28,7 @@ class FFT , psd_map(ctx.mm.get()) , demod_map(ctx.mm.get()) { - set_in_channel(0); + set_input_channel(0); set_scale_sch(0); set_fft_window(0); ctl.set_bit(); @@ -39,9 +39,9 @@ class FFT // Power Spectral Density ////////////////////////////////////// - void set_in_channel(uint32_t channel) { + void set_input_channel(uint32_t channel) { if (channel >= 2) { - ctx.log("FFT::set_in_channel invalid channel\n"); + ctx.log("FFT::set_input_channel invalid channel\n"); return; } diff --git a/examples/red-pitaya/fft/web/app.ts b/examples/red-pitaya/fft/web/app.ts index e367e37b6..d6479589d 100644 --- a/examples/red-pitaya/fft/web/app.ts +++ b/examples/red-pitaya/fft/web/app.ts @@ -1,25 +1,46 @@ class App { - public control: Control; + public plot: Plot; private fft: FFT; + public fftApp: FFTApp; public laserDriver: LaserDriver; public laserControl: LaserControl; - private navigation: Navigation; + private exportFile: ExportFile; + private imports: Imports; + public ddsFrequency: DDSFrequency; + private plotBasics: PlotBasics; + + private n_pts: number; + private x_min: number; + private x_max: number; + private y_min: number; + private y_max: number; constructor(window: Window, document: Document, ip: string, plot_placeholder: JQuery) { let client = new Client(ip, 5); - window.addEventListener('load', () => { + window.addEventListener('HTMLImportsLoaded', () => { client.init( () => { + this.imports = new Imports(document); this.fft = new FFT(client); this.fft.init( () => { - this.control = new Control(document, this.fft); - this.plot = new Plot(document, plot_placeholder, this.fft, this.control); + this.fftApp = new FFTApp(document, this.fft); + this.ddsFrequency = new DDSFrequency(document, this.fft); + + this.n_pts = this.fft.fft_size / 2; + this.x_min = 0; + this.x_max = this.fft.status.fs / 1E6 / 2; + this.y_min = -200; + this.y_max = 170; + + this.plotBasics = new PlotBasics(document, plot_placeholder, this.plot, this.n_pts, this.x_min, this.x_max, this.y_min, this.y_max, this.fft, "", "Frequency (MHz)"); + this.plot = new Plot(document, this.fft, this.plotBasics); + this.laserDriver = new LaserDriver(client); this.laserControl = new LaserControl(document, this.laserDriver); - this.navigation = new Navigation(document); + this.exportFile = new ExportFile(document, this.plot); }); }); }, false); diff --git a/examples/red-pitaya/fft/web/control.ts b/examples/red-pitaya/fft/web/control.ts deleted file mode 100644 index fd5050a5a..000000000 --- a/examples/red-pitaya/fft/web/control.ts +++ /dev/null @@ -1,95 +0,0 @@ -// Control widget -// (c) Koheron - -class Control { - private channelNum: number; - - private InChannelInputs: HTMLInputElement[]; - - public fftWindowIndex: number; - private fftWindowSelect: HTMLSelectElement; - - private frequencies: Array; - private frequencyInputs: HTMLInputElement[]; - private frequencySliders: HTMLInputElement[]; - - constructor(document: Document, private fft: FFT) { - this.channelNum = 2; - - this.InChannelInputs = []; - this.frequencyInputs = []; - this.frequencySliders = []; - - for (let i: number = 0; i < this.channelNum; i++) { - this.InChannelInputs[i] = document.getElementById('in-channel-' + i.toString()); - this.frequencyInputs[i] = document.getElementById('frequency-input-' + i.toString()); - this.frequencySliders[i] = document.getElementById('frequency-slider-' + i.toString()); - } - - this.frequencies = new Array(this.channelNum); - - this.fftWindowIndex = 1; - this.fftWindowSelect = document.getElementById("fft-window"); - this.init(); - } - - init() { - this.fft.getControlParameters( (sts: IFFTStatus) => { - for (let i: number = 0; i < this.channelNum; i++) { - this.frequencyInputs[i].value = (sts.dds_freq[0] / 1e6).toFixed(6); - this.frequencySliders[i].value = (sts.dds_freq[0] / 1e6).toFixed(4); - } - - this.fft.getFFTWindowIndex( (windowIndex: number) => { - this.fftWindowIndex = windowIndex; - this.fftWindowSelect.value = windowIndex.toString(); - - requestAnimationFrame( () => {this.update();} ) - }); - }); - } - - private update() { - this.fft.getControlParameters( (sts: IFFTStatus) => { - for (let i: number = 0; i < this.channelNum; i++) { - if (document.activeElement !== this.frequencyInputs[i]) { - this.frequencyInputs[i].value = (sts.dds_freq[i] / 1e6).toFixed(6); - } - - if (document.activeElement !== this.frequencySliders[i]) { - this.frequencySliders[i].value = (sts.dds_freq[i] / 1e6).toFixed(6); - this.frequencySliders[i].max = (sts.fs / 1e6 / 2).toFixed(1); - } - - this.InChannelInputs[sts.channel].checked = true; - - } - - this.fft.getFFTWindowIndex( (windowIndex: number) => { - this.fftWindowSelect.value = windowIndex.toString(); - requestAnimationFrame( () => { this.update(); } ) - }); - }); - } - - updateFrequency(channel: number, event) { - let frequencyValue = event.value; - - if (event.type === 'number') { - this.frequencySliders[channel].value = frequencyValue; - } else if (event.type === 'range') { - this.frequencyInputs[channel].value = frequencyValue; - } - - this.fft.setDDSFreq(channel, 1e6 * parseFloat(frequencyValue)); - } - - setInChannel(channel: number) { - this.fft.setInChannel(channel); - } - - setFFTWindow(windowIndex: number) { - this.fft.setFFTWindow(windowIndex); - } - -} \ No newline at end of file diff --git a/examples/red-pitaya/fft/web/export-file/export-file.html b/examples/red-pitaya/fft/web/export-file/export-file.html new file mode 100644 index 000000000..5fc5d0ece --- /dev/null +++ b/examples/red-pitaya/fft/web/export-file/export-file.html @@ -0,0 +1,13 @@ + \ No newline at end of file diff --git a/examples/red-pitaya/fft/web/export-file/export-file.ts b/examples/red-pitaya/fft/web/export-file/export-file.ts new file mode 100644 index 000000000..809b15a26 --- /dev/null +++ b/examples/red-pitaya/fft/web/export-file/export-file.ts @@ -0,0 +1,66 @@ +// Export file widget +// (c) Koheron + +class ExportFile { + + private exportDataButtons: HTMLButtonElement[]; + private exportPlotButtons: HTMLButtonElement[]; + + constructor(document: Document, private plot_) { + this.exportDataButtons = document.getElementsByClassName("export-data"); + this.exportPlotButtons = document.getElementsByClassName("export-plot"); + this.initExportData(); + this.initExportPlot(); + } + + initExportData(): void { + for (let i = 0; i < this.exportDataButtons.length; i++) { + this.exportDataButtons[i].addEventListener('click', (event) => { + + let csvContent = "data:text/csv;charset=utf-8,"; + let dateTime = new Date(); + let fftWindowIndex: string = (document.querySelector("[data-command='setFFTWindow']")).value; + let inputChannel: string = (document.querySelector("[name='input-channel']:checked")).value; + let ddsInputs = document.querySelectorAll(".dds-channel-input[type='range']"); + + csvContent += "Koheron FFT \n"; + csvContent += dateTime.getDate() + "/" + (dateTime.getMonth()+1) + "/" + dateTime.getFullYear() + " " ; + csvContent += dateTime.getHours() + ":" + dateTime.getMinutes() + ":" + dateTime.getSeconds() + "\n\n"; + csvContent += '"Window",' + fftWindowIndex + "\n"; + csvContent += '"Input channel",' + inputChannel + "\n"; + + for (let i: number = 0; i < ddsInputs.length; i++) { + let channel: string = ddsInputs[i].dataset.channel; + csvContent += '"Channel ' + channel + ' DDS frequency (MHz)",' + ddsInputs[i].value + "\n"; + } + + let yUnit: string = (document.querySelector(".unit-input:checked")).value; + csvContent += '"Frequency (MHz)",' + this.plot_.yLabel +' (' + yUnit.replace("-", "/") + ')" \n'; + + this.plot_.plot_data.forEach( (rowArray) => { + let row = rowArray.join(","); + csvContent += row + "\n"; + }); + + let exportGroup = this.exportDataButtons[i].parentElement; + let exportLink = (exportGroup.getElementsByTagName("a")[0]); + exportLink.href = encodeURI(csvContent); + exportLink.click(); + }) + } + } + + initExportPlot(): void { + for (let i = 0; i < this.exportPlotButtons.length; i++) { + this.exportPlotButtons[i].addEventListener('click', (event) => { + let canvas = this.plot_.plotBasics.plot.getCanvas(); + let imagePng = canvas.toDataURL("image/png").replace("image/png", "image/octet-stream"); + let exportGroup = this.exportPlotButtons[i].parentElement; + let exportLink = (exportGroup.getElementsByTagName("a")[0]); + exportLink.href = imagePng; + exportLink.click(); + }) + } + } + +} diff --git a/examples/red-pitaya/fft/web/fft.ts b/examples/red-pitaya/fft/web/fft.ts index 5de6aa10c..a2aa3b9ee 100644 --- a/examples/red-pitaya/fft/web/fft.ts +++ b/examples/red-pitaya/fft/web/fft.ts @@ -54,6 +54,12 @@ class FFT { (size) => {cb(size)}); } + read_psd_raw(cb: (psd: Float32Array) => void): void { + this.client.readFloat32Array(Command(this.id, this.cmds['read_psd_raw']), (psd: Float32Array) => { + cb(psd); + }); + } + read_psd(cb: (psd: Float32Array) => void): void { this.client.readFloat32Array(Command(this.id, this.cmds['read_psd']), (psd: Float32Array) => { cb(psd); @@ -64,8 +70,8 @@ class FFT { this.client.send(Command(this.id, this.cmds['set_dds_freq'], channel, freq_hz)); } - setInChannel(channel: number): void { - this.client.send(Command(this.id, this.cmds['set_in_channel'], channel)); + setInputChannel(channel: number): void { + this.client.send(Command(this.id, this.cmds['set_input_channel'], channel)); } setFFTWindow(windowIndex: number): void { @@ -91,4 +97,4 @@ class FFT { cb(windowIndex); }); } -} \ No newline at end of file +} diff --git a/examples/red-pitaya/fft/web/fft/fft-app.ts b/examples/red-pitaya/fft/web/fft/fft-app.ts new file mode 100644 index 000000000..48edcf68d --- /dev/null +++ b/examples/red-pitaya/fft/web/fft/fft-app.ts @@ -0,0 +1,71 @@ +// Control widget +// (c) Koheron + +class FFTApp { + private channelNum: number = 2; + private fftSelects: HTMLSelectElement[]; + private fftInputs: HTMLInputElement[]; + + constructor(document: Document, private fft: FFT) { + this.fftSelects = document.getElementsByClassName("fft-select"); + this.initFFTSelects(); + this.fftInputs = document.getElementsByClassName("fft-input"); + this.initFFTInputs(); + + this.updateFFTWindowInputs(); + this.updateControls(); + } + + // Updaters + + private updateControls() { + this.fft.getControlParameters( (sts: IFFTStatus) => { + for (let i = 0; i < this.channelNum; i++) { + let inputs = document.querySelectorAll(".dds-channel-input[data-command='setDDSFreq'][data-channel='" + i.toString() + "']"); + let inputsArray = []; + for (let j = 0; j < inputs.length; j++) { + inputsArray.push(inputs[j]); + } + + if (inputsArray.indexOf(document.activeElement) == -1) { + for (let j = 0; j < inputs.length; j++) { + inputs[j].value = (sts.dds_freq[i] / 1e6).toFixed(6); + if (inputs[j].type == "range") { + inputs[j].max = (sts.fs / 1e6 / 2).toFixed(1); + } + } + } + } + + (document.querySelector("[data-command='setInputChannel'][value='" + sts.channel.toString() + "']")).checked = true; + + requestAnimationFrame( () => { this.updateControls(); } ) + }); + } + + private updateFFTWindowInputs() { + this.fft.getFFTWindowIndex( (windowIndex: number) => { + (document.querySelector("[data-command='setFFTWindow']")).value = windowIndex.toString(); + requestAnimationFrame( () => { this.updateFFTWindowInputs(); } ) + }); + } + + // Setters + + initFFTSelects(): void { + for (let i = 0; i < this.fftSelects.length; i++) { + this.fftSelects[i].addEventListener('change', (event) => { + this.fft[(event.currentTarget).dataset.command]((event.currentTarget).value); + }) + } + } + + initFFTInputs(): void { + for (let i = 0; i < this.fftInputs.length; i++) { + this.fftInputs[i].addEventListener('change', (event) => { + this.fft[(event.currentTarget).dataset.command]((event.currentTarget).value); + }) + } + } + +} \ No newline at end of file diff --git a/examples/red-pitaya/fft/web/fft/fft-window.html b/examples/red-pitaya/fft/web/fft/fft-window.html new file mode 100644 index 000000000..488f62ea7 --- /dev/null +++ b/examples/red-pitaya/fft/web/fft/fft-window.html @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/examples/red-pitaya/fft/web/fft/input-channel.html b/examples/red-pitaya/fft/web/fft/input-channel.html new file mode 100644 index 000000000..cde6ef153 --- /dev/null +++ b/examples/red-pitaya/fft/web/fft/input-channel.html @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/examples/red-pitaya/fft/web/index.html b/examples/red-pitaya/fft/web/index.html index e106a6bd0..96f8f11d2 100644 --- a/examples/red-pitaya/fft/web/index.html +++ b/examples/red-pitaya/fft/web/index.html @@ -12,245 +12,54 @@ + + + - - + + + + + + + + + - +
-
-
Frequency (MHz)
- - - - - +
-
- - - -
- - - -
- dBm / Hz -
- -
- dBm -
- -
- nV / rtHz -
- -
- - - -
- - - - - -
- - - -
- - - -
- 0 -
- -
- 1 -
- -
- -
- - -
- +
+
+
+
+
- - -
- -
- - DDS Frequency (MHz) - -
- - - - - - - - - - - - -
- OUT 0 - - - - -
- OUT 1 - - - - -
- -
- -
- -
- File -
- -
- - - - - - - - - - -
- -
- -
- -
- Laser -
- - - - - - - -
- - - - - - -
- - - - - - - - - - - - - - - - - - -
- - - - - -
- - - -
Power (µW) - - - -
- -
+
+
+
diff --git a/examples/red-pitaya/fft/web/plot.ts b/examples/red-pitaya/fft/web/plot.ts deleted file mode 100644 index a5422ce52..000000000 --- a/examples/red-pitaya/fft/web/plot.ts +++ /dev/null @@ -1,428 +0,0 @@ -// Plot widget -// (c) Koheron - -class Plot { - private n_pts: number; - - private min_y: number = -200; - private max_y: number = 170; - - private range_x: jquery.flot.range; - private range_y: jquery.flot.range; - - private reset_range: boolean; - private options: jquery.flot.plotOptions; - private plot: jquery.flot.plot; - - private yUnit: string; - private yLabel: string; - - private plot_data: Array>; - - private isMeasure: boolean = true; - - private isPeakDetection: boolean = true; - private peakDatapointSpan: HTMLSpanElement; - private peakDatapoint: number[]; - - private hoverDatapointSpan: HTMLSpanElement; - private hoverDatapoint: number[]; - - private clickDatapointSpan: HTMLSpanElement; - private clickDatapoint: number[]; - - private exportDataFilename: HTMLLinkElement; - private exportPlotFilename: HTMLLinkElement; - - constructor(document: Document, private plot_placeholder: JQuery, private fft: FFT, private control: Control) { - this.setPlot(); - - this.range_x = {}; - this.range_x.from = 0; - this.range_x.to = this.fft.status.fs / 1E6 / 2; - - this.range_y = {}; - this.range_y.from = this.min_y; - this.range_y.to = this.max_y; - - this.n_pts = this.fft.fft_size / 2; - - this.yUnit = "dBm-Hz"; - this.yLabel = "Power Spectral Density"; - - this.hoverDatapointSpan = document.getElementById("hover-datapoint"); - this.hoverDatapoint = []; - - this.clickDatapointSpan = document.getElementById("click-datapoint"); - this.clickDatapoint = []; - - this.peakDatapointSpan = document.getElementById("peak-datapoint"); - this.peakDatapoint = []; - - this.exportDataFilename = document.getElementById("export-data-filename"); - this.exportPlotFilename = document.getElementById("export-plot-filename"); - - this.plot_data = []; - - this.update_plot(); - } - - update_plot() { - this.fft.read_psd( (psd: Float32Array) => { - this.redraw(psd, () => { - requestAnimationFrame( () => { this.update_plot(); } ); - }); - }); - } - - setPlot() { - this.reset_range = false; - - let labelAttribute: string = ""; - labelAttribute += " style='font-size: 16px; color: #333'"; - - this.options = { - canvas: true, - series: { - shadowSize: 0 // Drawing is faster without shadows - }, - yaxis: { - min: this.min_y, - max: this.max_y - }, - xaxis: { - min: 0, - max: this.fft.status.fs / 1E6 / 2, - show: true - }, - grid: { - margin: { - top: 0, - left: 0, - }, - borderColor: "#d5d5d5", - borderWidth: 1, - clickable: this.isMeasure, - hoverable: this.isMeasure, - autoHighlight: true - }, - selection: { - mode: "xy" - }, - colors: ["#019cd5", "#006400"], - legend: { - show: true, - noColumns: 0, - labelFormatter: (label: string, series: any): string => { - return "" + label + "\t" - }, - margin: 0, - position: "ne", - } - } - - this.rangeSelect(); - this.dblClick(); - this.onWheel(); - this.showHoverPoint(); - this.showClickPoint(); - this.plotLeave(); - this.reset_range = true; - } - - rangeSelect() { - this.plot_placeholder.bind("plotselected", (event: JQueryEventObject, - ranges: jquery.flot.ranges) => { - // Clamp the zooming to prevent external zoom - if (ranges.xaxis.to - ranges.xaxis.from < 0.00001) { - ranges.xaxis.to = ranges.xaxis.from + 0.00001; - } - - if (ranges.yaxis.to - ranges.yaxis.from < 0.00001) { - ranges.yaxis.to = ranges.yaxis.from + 0.00001; - } - - this.range_x.from = ranges.xaxis.from; - this.range_x.to = ranges.xaxis.to; - - this.range_y.from = ranges.yaxis.from; - this.range_y.to = ranges.yaxis.to; - - this.resetRange(); - }); - } - - // A double click on the plot resets to full span - dblClick() { - this.plot_placeholder.bind("dblclick", (evt: JQueryEventObject) => { - this.range_x.from = 0; - this.range_x.to = this.fft.status.fs / 1E6 / 2; - - this.range_y = {}; - this.resetRange(); - }); - } - - resetRange() { - this.reset_range = true; - } - - convertValue(inValue: number, outUnit: string): number { - // inValue in W / Hz - let outValue: number = 0; - - if (outUnit === "dBm-Hz") { - outValue = 10 * Math.log(inValue / 1E-3) / Math.LN10; - } else if (outUnit === "dBm") { - outValue = 10 * Math.log(inValue * (this.fft.status.W2 / this.fft.status.W1) * this.fft.status.fs / this.fft.fft_size / 1E-3) / Math.LN10; - } else if (outUnit === "nv-rtHz") { - outValue = Math.sqrt(50 * inValue) * 1E9; - } - - return outValue; - } - - updateDatapointSpan(datapoint: number[], datapointSpan: HTMLSpanElement): void { - let positionX: number = (this.plot.pointOffset({x: datapoint[0], y: datapoint[1] })).left; - let positionY: number = (this.plot.pointOffset({x: datapoint[0], y: datapoint[1] })).top; - - datapointSpan.innerHTML = "(" + (datapoint[0].toFixed(2)).toString() + "," + datapoint[1].toFixed(2).toString() + ")"; - - if (datapoint[0] < (this.range_x.from + this.range_x.to) / 2) { - datapointSpan.style.left = (positionX + 5).toString() + "px"; - } else { - datapointSpan.style.left = (positionX - 140).toString() + "px"; - } - - if (datapoint[1] < ( (this.range_y.from + this.range_y.to) / 2 ) ) { - datapointSpan.style.top = (positionY - 50).toString() + "px"; - } else { - datapointSpan.style.top = (positionY + 5).toString() + "px"; - } - } - - redraw(psd: Float32Array, callback: () => void) { - // let plot_data: Array> = []; - - this.peakDatapoint = [ this.fft.status.fs / 1E6 / 2 / this.n_pts , this.convertValue(psd[0], this.yUnit)]; - - for (let i: number = 0; i <= this.n_pts; i++) { - - let freq: number = (i + 1) * this.fft.status.fs / 1E6 / 2 / this.n_pts; // MHz - let convertedPsd: number = this.convertValue(psd[i], this.yUnit); - this.plot_data[i] = [freq, convertedPsd]; - - if (this.peakDatapoint[1] < this.plot_data[i][1]) { - this.peakDatapoint[0] = this.plot_data[i][0]; - this.peakDatapoint[1] = this.plot_data[i][1]; - } - - } - - const plt_data: jquery.flot.dataSeries[] = [{label: this.yLabel, data: this.plot_data}]; - - if (this.reset_range) { - this.options.xaxis.min = this.range_x.from; - this.options.xaxis.max = this.range_x.to; - this.options.yaxis.min = this.range_y.from; - this.options.yaxis.max = this.range_y.to; - this.plot = $.plot(this.plot_placeholder, plt_data, this.options); - this.plot.setupGrid(); - - this.range_y.from = this.plot.getAxes().yaxis.min; - this.range_y.to = this.plot.getAxes().yaxis.max; - - this.reset_range = false; - } else { - this.plot.setData(plt_data); - this.plot.draw(); - } - - let localData: jquery.flot.dataSeries[] = this.plot.getData(); - - setTimeout(this.plot.unhighlight(), 100); - - if (this.clickDatapoint.length > 0) { - - for (var i: number = 0; i < this.n_pts; i++) { - if ( localData[0]['data'][i][0] > this.clickDatapoint[0] ) { - break; - } - } - - var p1 = localData[0]['data'][i-1]; - var p2 = localData[0]['data'][i]; - - if (p1 == null) { - this.clickDatapoint[1] = p2[1]; - } else if (p2 == null) { - this.clickDatapoint[1] = p1[1]; - } else { - this.clickDatapoint[1] = p1[1] + (p2[1] - p1[1]) * (this.clickDatapoint[0] - p1[0]) / (p2[0] - p1[0]); - } - - if ( this.range_x.from < this.clickDatapoint[0] && this.clickDatapoint[0] < this.range_x.to && - this.range_y.from < this.clickDatapoint[1] && this.clickDatapoint[1] < this.range_y.to ) { - this.updateDatapointSpan(this.clickDatapoint, this.clickDatapointSpan); - this.clickDatapointSpan.style.display = "inline-block"; - this.plot.highlight(localData[0], this.clickDatapoint); - } else { - this.clickDatapointSpan.style.display = "none"; - } - - } - - if (this.isPeakDetection) { - - this.plot.unhighlight(localData[0], this.peakDatapoint); - - if (this.range_x.from < this.peakDatapoint[0] && this.peakDatapoint[0] < this.range_x.to && - this.range_y.from < this.peakDatapoint[1] && this.peakDatapoint[1] < this.range_y.to ) { - this.updateDatapointSpan(this.peakDatapoint, this.peakDatapointSpan); - this.plot.highlight(localData[0], this.peakDatapoint); - this.peakDatapointSpan.style.display = "inline-block"; - } else { - - this.plot.unhighlight(localData[0], this.peakDatapoint); - this.peakDatapointSpan.style.display = "none"; - } - - } else { - - this.plot.unhighlight(localData[0], this.peakDatapoint); - this.peakDatapointSpan.style.display = "none"; - - } - - callback(); - } - - onWheel(): void { - this.plot_placeholder.bind("wheel", (evt: JQueryEventObject) => { - let delta: number = (evt.originalEvent).deltaX - + (evt.originalEvent).deltaY; - delta /= Math.abs(delta); - - const zoomRatio: number = 0.2; - - if ((evt.originalEvent).shiftKey) { // Zoom Y - const positionY: number = (evt.originalEvent).pageY - this.plot.offset().top; - const y0: any = this.plot.getAxes().yaxis.c2p(positionY); - - this.range_y = { - from: y0 - (1 + zoomRatio * delta) * (y0 - this.plot.getAxes().yaxis.min), - to: y0 - (1 + zoomRatio * delta) * (y0 - this.plot.getAxes().yaxis.max) - }; - - this.resetRange(); - return false; - } else if ((evt.originalEvent).altKey) { // Zoom X - const positionX: number = (evt.originalEvent).pageX - this.plot.offset().left; - const x0: any = this.plot.getAxes().xaxis.c2p(positionX); - - if (x0 < 0 || x0 > this.fft.status.fs / 1E6 / 2) { - return; - } - - this.range_x = { - from: Math.max(x0 - (1 + zoomRatio * delta) * (x0 - this.plot.getAxes().xaxis.min), 0), - to: Math.min(x0 - (1 + zoomRatio * delta) * (x0 - this.plot.getAxes().xaxis.max), this.fft.status.fs / 1E6 / 2) - }; - - this.resetRange(); - return false; - } - - return true - }); - } - - changeYUnit(yUnit: string): void { - this.yUnit = yUnit; - this.resetRange(); - } - - detectPeak(): void { - if (this.isPeakDetection) { - this.isPeakDetection = false; - } else { - this.isPeakDetection = true; - } - } - - showHoverPoint(): void { - this.plot_placeholder.bind("plothover", (event: JQueryEventObject, pos, item) => { - if (item) { - - this.hoverDatapoint[0] = item.datapoint[0]; - this.hoverDatapoint[1] = item.datapoint[1]; - - this.hoverDatapointSpan.style.display = "inline-block"; - this.updateDatapointSpan(this.hoverDatapoint, this.hoverDatapointSpan); - - } else { - this.hoverDatapointSpan.style.display = "none"; - } - - }); - } - - showClickPoint(): void { - this.plot_placeholder.bind("plotclick", (event: JQueryEventObject, pos, item) => { - if (item) { - - this.clickDatapoint[0] = item.datapoint[0]; - this.clickDatapoint[1] = item.datapoint[1]; - - this.clickDatapointSpan.style.display = "inline-block"; - this.updateDatapointSpan(this.clickDatapoint, this.clickDatapointSpan); - - this.plot.unhighlight(); - this.plot.highlight(item.series, this.clickDatapoint); - - } - }); - } - - plotLeave(): void { - this.plot_placeholder.bind("mouseleave", (event: JQueryEventObject, pos, item) => { - this.hoverDatapointSpan.style.display = "none"; - }); - } - - exportData() { - let csvContent = "data:text/csv;charset=utf-8,"; - - csvContent += "Koheron Alpha \n"; - - var dateTime = new Date(); - csvContent += dateTime.getDate() + "/" + (dateTime.getMonth()+1) + "/" + dateTime.getFullYear() + " " ; - csvContent += dateTime.getHours() + ":" + dateTime.getMinutes() + ":" + dateTime.getSeconds() + "\n"; - - csvContent += "\n"; - csvContent += '"Window",' + (this.control.fftWindowIndex).toString() + "\n"; - csvContent += '"Input channel",' + (this.fft.status.channel).toString() + "\n"; - csvContent += '"Channel 0 DDS frequency (MHz)",' + (this.fft.status.dds_freq[0] / 1e6).toString() + "\n"; - csvContent += '"Channel 1 DDS frequency (MHz)",' + (this.fft.status.dds_freq[1] / 1e6).toString() + "\n"; - - csvContent += "\n\n"; - - csvContent += '"Frequency (MHz)","Power spectral density (' + this.yUnit.replace("-", "/") + ')" \n'; - - this.plot_data.forEach((rowArray) => { - let row = rowArray.join(","); - csvContent += row + "\n"; - }); - - this.exportDataFilename.href = encodeURI(csvContent); - this.exportDataFilename.click(); - } - - exportPlot(): void { - var canvas = this.plot.getCanvas(); - var imagePng = canvas.toDataURL("image/png").replace("image/png", "image/octet-stream"); - - this.exportPlotFilename.href = imagePng; - this.exportPlotFilename.click(); - } -} \ No newline at end of file diff --git a/examples/red-pitaya/fft/web/plot/peak-detection.html b/examples/red-pitaya/fft/web/plot/peak-detection.html new file mode 100644 index 000000000..2d7473036 --- /dev/null +++ b/examples/red-pitaya/fft/web/plot/peak-detection.html @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/examples/red-pitaya/fft/web/plot/plot.ts b/examples/red-pitaya/fft/web/plot/plot.ts new file mode 100644 index 000000000..25ddeda3f --- /dev/null +++ b/examples/red-pitaya/fft/web/plot/plot.ts @@ -0,0 +1,56 @@ +// Plot widget +// (c) Koheron + +class Plot { + public n_pts: number; + public plot: jquery.flot.plot; + public plot_data: Array>; + + public yLabel: string = "Power Spectral Density"; + private peakDatapoint: number[]; + + public x_max: number; + + constructor(document: Document, private fft: FFT, private plotBasics: PlotBasics) { + + this.n_pts = this.fft.fft_size / 2; + this.x_max = this.fft.status.fs / 1E6 / 2; + + this.peakDatapoint = []; + this.plot_data = []; + + this.updatePlot(this.x_max); + } + + updatePlot(max_x: number) { + this.fft.read_psd( (psd: Float32Array) => { + let yUnit: string = (document.querySelector(".unit-input:checked")).value; + this.peakDatapoint = [ max_x / this.n_pts , this.convertValue(psd[0], yUnit)]; + + for (let i: number = 0; i <= this.n_pts; i++) { + let freq: number = (i + 1) * max_x / this.n_pts; // MHz + let convertedPsd: number = this.convertValue(psd[i], yUnit); + this.plot_data[i] = [freq, convertedPsd]; + }; + + this.plotBasics.redraw(this.plot_data, this.n_pts, this.peakDatapoint, this.yLabel, () => { + requestAnimationFrame( () => { this.updatePlot(max_x); } ); + }); + }); + } + + convertValue(inValue: number, outUnit: string): number { + // inValue in W / Hz + let outValue: number = 0; + + if (outUnit === "dBm-Hz") { + outValue = 10 * Math.log(inValue / 1E-3) / Math.LN10; + } else if (outUnit === "dBm") { + outValue = 10 * Math.log(inValue * (this.fft.status.W2 / this.fft.status.W1) * this.fft.status.fs / this.fft.fft_size / 1E-3) / Math.LN10; + } else if (outUnit === "nv-rtHz") { + outValue = Math.sqrt(50 * inValue) * 1E9; + } + + return outValue; + } +} diff --git a/examples/red-pitaya/fft/web/plot/yunit.html b/examples/red-pitaya/fft/web/plot/yunit.html new file mode 100644 index 000000000..d19805eec --- /dev/null +++ b/examples/red-pitaya/fft/web/plot/yunit.html @@ -0,0 +1,12 @@ + \ No newline at end of file diff --git a/examples/red-pitaya/laser-controller/app.ts b/examples/red-pitaya/laser-controller/app.ts index 763ddf8d5..d3e524385 100644 --- a/examples/red-pitaya/laser-controller/app.ts +++ b/examples/red-pitaya/laser-controller/app.ts @@ -2,20 +2,20 @@ class App { public laserControl: LaserControl; private laserDriver: LaserDriver; - private navigation: Navigation; + private imports: Imports; constructor(window: Window, document: Document, ip: string) { let client = new Client(ip, 5); - - window.addEventListener('load', () => { + window.addEventListener('HTMLImportsLoaded', () => { client.init( () => { + this.imports = new Imports(document); this.laserDriver = new LaserDriver(client); this.laserControl = new LaserControl(document, this.laserDriver); - this.navigation = new Navigation(document); }); }, false); window.onbeforeunload = () => { client.exit(); }; + } } diff --git a/examples/red-pitaya/laser-controller/config.yml b/examples/red-pitaya/laser-controller/config.yml index 07e0f7fb5..a7057f4a7 100644 --- a/examples/red-pitaya/laser-controller/config.yml +++ b/examples/red-pitaya/laser-controller/config.yml @@ -1,7 +1,7 @@ --- name: laser-controller board: boards/red-pitaya -version: 0.1.0 +version: 0.1.1 cores: - fpga/cores/redp_adc_v1_0 @@ -64,7 +64,7 @@ drivers: web: - web/koheron.ts - web/laser.ts + - web/laser-control.html - ./app.ts - ./index.html - - web/main.css - - web/navigation.ts \ No newline at end of file + - web/main.css \ No newline at end of file diff --git a/examples/red-pitaya/laser-controller/index.html b/examples/red-pitaya/laser-controller/index.html index b83518d96..d10db4ecb 100644 --- a/examples/red-pitaya/laser-controller/index.html +++ b/examples/red-pitaya/laser-controller/index.html @@ -12,6 +12,8 @@ + + @@ -19,86 +21,18 @@ - - - - +
- -
- -
- Laser -
- - - - - - - -
- - - - - - -
- - - - - - - - - - - - - - - - - - -
- - - - - -
- - - -
Power (µW)
- -
- +
diff --git a/examples/red-pitaya/led-blinker/config.yml b/examples/red-pitaya/led-blinker/config.yml index 81acaf549..32596d3cc 100644 --- a/examples/red-pitaya/led-blinker/config.yml +++ b/examples/red-pitaya/led-blinker/config.yml @@ -1,7 +1,7 @@ --- name: led-blinker board: boards/red-pitaya -version: 0.1.0 +version: 0.1.1 cores: - fpga/cores/axi_ctl_register_v1_0 @@ -35,8 +35,6 @@ drivers: web: - ./web/index.html - web/koheron.ts - - ./web/led_blinker.ts + - web/led-blinker.ts - ./web/app.ts - - ./web/control.ts - - web/main.css - - web/navigation.ts \ No newline at end of file + - web/main.css \ No newline at end of file diff --git a/examples/red-pitaya/led-blinker/web/app.ts b/examples/red-pitaya/led-blinker/web/app.ts index 0745e94aa..03da95a9d 100644 --- a/examples/red-pitaya/led-blinker/web/app.ts +++ b/examples/red-pitaya/led-blinker/web/app.ts @@ -1,16 +1,16 @@ class App { private driver: LedBlinker; - public control: Control; - private navigation: Navigation; + public control: LedBlinkerControl; + private imports: Imports; constructor(window: Window, document: Document, ip: string) { let client = new Client(ip, 5); - window.addEventListener('load', () => { + window.addEventListener('HTMLImportsLoaded', () => { client.init( () => { + this.imports = new Imports(document); this.driver = new LedBlinker(client); - this.control = new Control(document, this.driver); - this.navigation = new Navigation(document); + this.control = new LedBlinkerControl(document, this.driver, 8); }); }, false); diff --git a/examples/red-pitaya/led-blinker/web/control.ts b/examples/red-pitaya/led-blinker/web/control.ts deleted file mode 100644 index 8a10ba769..000000000 --- a/examples/red-pitaya/led-blinker/web/control.ts +++ /dev/null @@ -1,41 +0,0 @@ -// LED Blinker -// (c) Koheron - -class Control { - - private ledBtns: HTMLInputElement[]; - private ledStatus: boolean[]; - - constructor(private document: Document, private driver: LedBlinker) { - - this.ledBtns = []; - this.ledStatus = []; - - for (let i: number = 0; i < 8 ; i++) { - this.ledBtns[i] = document.getElementById('led-' + i.toString()); - } - - this.update(); - - } - - update(): void { - this.driver.getLeds( (value) => { - for (let i: number = 0; i < 8 ; i++) { - this.ledStatus[i] = (((value >> i) % 2) === 1); - if (this.ledStatus[i]) { - this.ledBtns[i].value = 'OFF'; - } - else { - this.ledBtns[i].value = 'ON'; - } - }; - requestAnimationFrame( () => { this.update(); } ) - }); - } - - switchLed(index: number) { - this.driver.setLed(index, !this.ledStatus[index]); - } - -} diff --git a/examples/red-pitaya/led-blinker/web/index.html b/examples/red-pitaya/led-blinker/web/index.html index 052859444..710867c8b 100644 --- a/examples/red-pitaya/led-blinker/web/index.html +++ b/examples/red-pitaya/led-blinker/web/index.html @@ -15,70 +15,22 @@ + + + - +
-
- - - - - - - - - - - - - - - - - - - - - - - - - - -
LEDs01234567
ON/OFF
- -
+
- - diff --git a/examples/red-pitaya/led-blinker/web/led_blinker.ts b/examples/red-pitaya/led-blinker/web/led_blinker.ts deleted file mode 100644 index 9ce4f0224..000000000 --- a/examples/red-pitaya/led-blinker/web/led_blinker.ts +++ /dev/null @@ -1,21 +0,0 @@ -class LedBlinker { - private driver: Driver; - private id: number; - private cmds: HashTable; - - constructor (private client: Client) { - this.driver = this.client.getDriver('LedBlinker'); - this.id = this.driver.id; - this.cmds = this.driver.getCmds(); - } - - getLeds(cb: (value: number) => void): void { - this.client.readUint32(Command(this.id, this.cmds['get_leds']), - (value) => {cb(value)}); - } - - setLed(index:number, status: boolean): void { - this.client.send(Command(this.id, this.cmds['set_led'], index, status)); - } - -} \ No newline at end of file diff --git a/examples/red-pitaya/oscillo/config.yml b/examples/red-pitaya/oscillo/config.yml index c588bf968..ddbed117a 100644 --- a/examples/red-pitaya/oscillo/config.yml +++ b/examples/red-pitaya/oscillo/config.yml @@ -1,7 +1,7 @@ --- name: oscillo board: boards/red-pitaya -version: 0.1.0 +version: 0.1.1 # ------------------------------------------------------------ # Memory @@ -102,15 +102,18 @@ drivers: # ------------------------------------------------------------ web: - - web/koheron.ts - web/jquery.flot.d.ts - web/laser.ts + - web/laser-control.html - ./web/oscillo.ts - ./web/app.ts - web/average.ts + - web/average.html - web/modulation.ts + - web/modulation.html + - web/plot-basics/plot-basics.ts + - web/plot-basics/plot-basics.html - ./web/plot.ts - ./web/index.html - - web/main.css - - web/navigation.ts \ No newline at end of file + - web/main.css \ No newline at end of file diff --git a/examples/red-pitaya/oscillo/web/app.ts b/examples/red-pitaya/oscillo/web/app.ts index 668d63457..3e061c11f 100644 --- a/examples/red-pitaya/oscillo/web/app.ts +++ b/examples/red-pitaya/oscillo/web/app.ts @@ -1,23 +1,31 @@ class App { - public laserDriver: LaserDriver; - public laserControl: LaserControl; - public oscillo: Oscillo; - public modulationDriver: ModulationDriver; - public modulationControl: ModulationControl; - public average: Average; - public plot: Plot; - private navigation: Navigation; + private laserDriver: LaserDriver; + private laserControl: LaserControl; + private oscillo: Oscillo; + private modulationDriver: ModulationDriver; + private modulationControl: ModulationControl; + private average: Average; + private plot: Plot; + private imports: Imports; + private plotBasics: PlotBasics; wfmSize = 8192; samplingRate = 125e6; + private n_pts: number; + private x_min: number; + private x_max: number; + private y_min: number; + private y_max: number; + constructor(window: Window, document: Document, ip: string, plot_placeholder: JQuery) { let client = new Client(ip, 5); - window.addEventListener('load', () => { + window.addEventListener('HTMLImportsLoaded', () => { client.init( () => { + this.imports = new Imports(document); this.laserDriver = new LaserDriver(client); this.laserControl = new LaserControl(document, this.laserDriver); this.oscillo = new Oscillo(client); @@ -26,8 +34,14 @@ class App { this.modulationDriver = new ModulationDriver(client); this.modulationControl = new ModulationControl(document, this.modulationDriver, this.wfmSize, this.samplingRate); - this.plot = new Plot(document, plot_placeholder, this.oscillo); - this.navigation = new Navigation(document); + this.n_pts = 16384; + this.x_min = 0; + this.x_max = this.oscillo.maxT; + this.y_min = -8192; + this.y_max = +8191; + + this.plotBasics = new PlotBasics(document, plot_placeholder, this.plot, this.n_pts, this.x_min, this.x_max, this.y_min, this.y_max, this.oscillo, "setTimeRange", "Time (µs)"); + this.plot = new Plot(document, this.oscillo, this.plotBasics); }); }, false); diff --git a/examples/red-pitaya/oscillo/web/index.html b/examples/red-pitaya/oscillo/web/index.html index caaa7651f..01fcc279b 100644 --- a/examples/red-pitaya/oscillo/web/index.html +++ b/examples/red-pitaya/oscillo/web/index.html @@ -12,6 +12,8 @@ + + @@ -20,21 +22,17 @@ + + + + + + - +
@@ -42,243 +40,19 @@
-
Time (µs)
+
- Channel 1 Channel 2 - -
- -
- -
- Laser -
- - - - - - - -
- - - - - - -
- - - - - - - - - - - - - - - - - - -
- - - - - -
- - - -
Power (µW) - - - -
- -
- -
- -
- Average -
- - - - - - - - - -
- - - Count: - - Minimum: - -
-
-
- -
- Modulation -
- -
- -
- 1 -
-
- 2 -
-
- -
-
- -
- Sine -
-
- Triangle -
-
- Square -
-
- - - - - - - - - - - - - - - - - -
Amplitude (arb. units) - - - -
Frequency (MHz) - - - -
Offset (arb. units) - - - -
-
- - - -
+
+
+
diff --git a/examples/red-pitaya/oscillo/web/plot.ts b/examples/red-pitaya/oscillo/web/plot.ts index 4db0484c7..0de01779d 100644 --- a/examples/red-pitaya/oscillo/web/plot.ts +++ b/examples/red-pitaya/oscillo/web/plot.ts @@ -5,53 +5,20 @@ class Plot { private ch1Checkbox: HTMLInputElement; private ch2Checkbox: HTMLInputElement; + private rangeFunction: string; - private plot: jquery.flot.plot; - private options: jquery.flot.plotOptions; - private isResetRange: boolean; - - private minY: number; - private maxY: number; - private rangeY: jquery.flot.range; - - private hoverDatapointSpan: HTMLSpanElement; - private hoverDatapoint: number[]; - - private clickDatapointSpan: HTMLSpanElement; - private clickDatapoint: number[]; - - constructor(document: Document, private plot_placeholder: JQuery, private driver: Oscillo) { + constructor(document: Document, private driver, private plotBasics: PlotBasics) { this.ch1Checkbox = document.getElementById('ch1-checkbox'); this.ch2Checkbox = document.getElementById('ch2-checkbox'); - - this.setPlot(); - - this.minY = -8192; - this.maxY = +8191; - - this.rangeY = { - from: this.minY, - to: this.maxY - }; - - this.driver.setTimeRange({ - from : 0, - to : this.driver.maxT - }); - - this.hoverDatapointSpan = document.getElementById("hover-datapoint"); - this.hoverDatapoint = []; - - this.clickDatapointSpan = document.getElementById("click-datapoint"); - this.clickDatapoint = []; - + this.rangeFunction = "setTimeRange"; this.updatePlot(); - } updatePlot() { - this.driver.getDecimatedData( (ch0: number[][], ch1: number[][], rangeX: jquery.flot.range) => { - this.redraw(ch0, ch1, rangeX, () => { + let is_channel_1: boolean = this.ch1Checkbox.checked; + let is_channel_2: boolean = this.ch2Checkbox.checked; + this.driver.getDecimatedData( (ch0: number[][], ch1: number[][], range_x: jquery.flot.range) => { + this.plotBasics.redrawTwoChannels(ch0, ch1, range_x, "Channel 1", "Channel 2", is_channel_1, is_channel_2, () => { requestAnimationFrame( () => { this.updatePlot(); }); @@ -59,185 +26,4 @@ class Plot { }); } - // == Plot - - setPlot(): void { - this.isResetRange = false; - - this.options = { - series: { - shadowSize: 0 // Drawing is faster without shadows - }, - yaxis: { - min: this.minY, - max: this.maxY - }, - xaxis: { - min: 0, - max: this.driver.maxT, - show: true - }, - grid: { - margin: { - top: 0, - left: 0, - }, - borderColor: "#d5d5d5", - borderWidth: 1 - }, - selection: { - mode: 'xy' - }, - colors: ['#019cd5', '#d53a01'] - } - - this.rangeSelect(); - this.dblClick(); - this.onWheel(); - this.isResetRange = true; - } - - rangeSelect(): void { - this.plot_placeholder.bind('plotselected', (event: JQueryEventObject, - ranges: jquery.flot.ranges) => { - // Clamp the zooming to prevent external zoom - if (ranges.xaxis.to - ranges.xaxis.from < 0.00001) { - ranges.xaxis.to = ranges.xaxis.from + 0.00001; - } - - if (ranges.yaxis.to - ranges.yaxis.from < 0.00001) { - ranges.yaxis.to = ranges.yaxis.from + 0.00001; - } - - this.rangeY = { - from: ranges.yaxis.from, - to: ranges.yaxis.to - }; - - this.driver.setTimeRange(ranges.xaxis); - this.resetRange(); - }); - } - - // A double click on the plot resets to full span - dblClick(): void { - this.plot_placeholder.bind('dblclick', (evt: JQueryEventObject) => { - const rangeX: jquery.flot.range = { - from : 0, - to : this.driver.maxT - }; - - this.rangeY = {}; - this.driver.setTimeRange(rangeX); - this.resetRange(); - }); - } - - autoScale(): void { - const rangeX = { - from : 0, - to : this.driver.maxT - }; - - this.rangeY = {}; - this.driver.setTimeRange(rangeX); - this.resetRange(); - } - - onWheel(): void { - this.plot_placeholder.bind('wheel', (evt: JQueryEventObject) => { - let delta: number = (evt.originalEvent).deltaX - + (evt.originalEvent).deltaY; - delta /= Math.abs(delta); - - const zoomRatio: number = 0.2; - - if ((evt.originalEvent).shiftKey) { // Zoom Y - const positionY: number = (evt.originalEvent).pageY - this.plot.offset().top; - const y0: any = this.plot.getAxes().yaxis.c2p(positionY); - - this.rangeY = { - from: y0 - (1 + zoomRatio * delta) * (y0 - this.plot.getAxes().yaxis.min), - to: y0 - (1 + zoomRatio * delta) * (y0 - this.plot.getAxes().yaxis.max) - }; - - this.resetRange(); - return false; - } else if ((evt.originalEvent).altKey) { // Zoom X - const positionX: number = (evt.originalEvent).pageX - this.plot.offset().left; - const t0: any = this.plot.getAxes().xaxis.c2p(positionX); - - if (t0 < 0 || t0 > this.driver.maxT) { - return; - } - - // To avoid getting stuck at the edge - if (this.plot.getAxes().xaxis.max - t0 < 2e6 / this.driver.samplingRate) { - delta = 4 * delta; - } - - const rangeX: jquery.flot.range = { - from: Math.max(t0 - (1 + zoomRatio * delta) * (t0 - this.plot.getAxes().xaxis.min), 0), - to: Math.min(t0 - (1 + zoomRatio * delta) * (t0 - this.plot.getAxes().xaxis.max), this.driver.maxT) - }; - - this.driver.setTimeRange(rangeX); - this.resetRange(); - return false; - } - - return true - }); - } - - resetRange(): void { - this.isResetRange = true; - } - - redraw(ch0: number[][], ch1: number[][], - rangeX: jquery.flot.range, callback: () => void): void { - - if (ch0.length === 0 || ch1.length === 0) { - callback(); - return; - } - - let plotCh0: number[][] = []; - let plotCh1: number[][] = []; - - if (this.ch1Checkbox.checked && this.ch2Checkbox.checked) { - plotCh0 = ch0; - plotCh1 = ch1; - // plotData = [ch0, ch1]; - } else if (this.ch1Checkbox.checked && !this.ch2Checkbox.checked) { - plotCh0 = ch0; - plotCh1 = []; - } else if (!this.ch1Checkbox.checked && this.ch2Checkbox.checked) { - plotCh0 = []; - plotCh1 = ch1; - } else { - plotCh0 = []; - plotCh1 = []; - } - - const plt_data: jquery.flot.dataSeries[] = [{label: "Channel 1 - Voltage", data: plotCh0}, {label: "Channel 2 - Voltage", data: plotCh1}]; - - if (this.isResetRange) { - this.options.xaxis.min = rangeX.from; - this.options.xaxis.max = rangeX.to; - this.options.yaxis.min = this.rangeY.from; - this.options.yaxis.max = this.rangeY.to; - - this.plot = $.plot(this.plot_placeholder, plt_data, this.options); - - this.plot.setupGrid(); - this.isResetRange = false; - } else { - this.plot.setData(plt_data); - this.plot.draw(); - } - - callback(); - } - } \ No newline at end of file diff --git a/examples/red-pitaya/phase-noise-analyzer/config.yml b/examples/red-pitaya/phase-noise-analyzer/config.yml index d1486ad67..0be8e1c99 100644 --- a/examples/red-pitaya/phase-noise-analyzer/config.yml +++ b/examples/red-pitaya/phase-noise-analyzer/config.yml @@ -1,6 +1,7 @@ --- name: phase-noise-analyzer board: boards/red-pitaya +version: 0.1.0 cores: - fpga/cores/axi_ctl_register_v1_0 @@ -66,5 +67,4 @@ drivers: web: - web/index.html - web/main.css - - web/koheron.ts - - web/navigation.ts + - web/koheron.ts \ No newline at end of file diff --git a/examples/red-pitaya/pulse-generator/config.yml b/examples/red-pitaya/pulse-generator/config.yml index 66977b143..9ce58bca8 100644 --- a/examples/red-pitaya/pulse-generator/config.yml +++ b/examples/red-pitaya/pulse-generator/config.yml @@ -1,7 +1,7 @@ --- name: pulse-generator board: boards/red-pitaya -version: 0.1.1 +version: 0.1.2 cores: - fpga/cores/redp_adc_v1_0 @@ -57,6 +57,7 @@ web: - ./web/app.ts - ./web/control.ts - ./web/plot.ts + - web/plot-basics/plot-basics.ts + - web/plot-basics/plot-basics.html - ./web/index.html - - web/main.css - - web/navigation.ts \ No newline at end of file + - web/main.css \ No newline at end of file diff --git a/examples/red-pitaya/pulse-generator/web/app.ts b/examples/red-pitaya/pulse-generator/web/app.ts index 9d627370e..b361039d0 100644 --- a/examples/red-pitaya/pulse-generator/web/app.ts +++ b/examples/red-pitaya/pulse-generator/web/app.ts @@ -2,18 +2,33 @@ class App { public control: Control; public plot: Plot; private driver: PulseGenerator; - private navigation: Navigation; + private imports: Imports; + + private plotBasics: PlotBasics; + private n_pts: number; + private x_min: number; + private x_max: number; + private y_min: number; + private y_max: number; constructor(window: Window, document: Document, ip: string, plot_placeholder: JQuery) { let client = new Client(ip, 5); - window.addEventListener('load', () => { + window.addEventListener('HTMLImportsLoaded', () => { client.init( () => { + this.imports = new Imports(document); this.driver = new PulseGenerator(client); this.control = new Control(document, this.driver); - this.plot = new Plot(document, plot_placeholder, this.driver); - this.navigation = new Navigation(document); + + this.n_pts = 1024; + this.x_min = 0; + this.x_max = 1024; + this.y_min = -1.0; + this.y_max = 1.0; + + this.plotBasics = new PlotBasics(document, plot_placeholder, this.plot, this.n_pts, this.x_min, this.x_max, this.y_min, this.y_max, this.driver, "", "FIFO Sample Number"); + this.plot = new Plot(document, this.driver, this.plotBasics); }); }, false); diff --git a/examples/red-pitaya/pulse-generator/web/index.html b/examples/red-pitaya/pulse-generator/web/index.html index e7438f452..a4679a53b 100644 --- a/examples/red-pitaya/pulse-generator/web/index.html +++ b/examples/red-pitaya/pulse-generator/web/index.html @@ -12,6 +12,8 @@ + + @@ -19,28 +21,22 @@ - - - - +
-
FIFO sample number
+
diff --git a/examples/red-pitaya/pulse-generator/web/plot.ts b/examples/red-pitaya/pulse-generator/web/plot.ts index a57270632..73c4b6f46 100644 --- a/examples/red-pitaya/pulse-generator/web/plot.ts +++ b/examples/red-pitaya/pulse-generator/web/plot.ts @@ -2,192 +2,26 @@ // (c) Koheron class Plot { - private plot_x_max: number = 1024; - private min_y: number = -1.0; // Volts - private max_y: number = 1.0; // Volts - - private nPoints: number; - - private reset_range: boolean; - private options: jquery.flot.plotOptions; - private plot: jquery.flot.plot; - private zoom_x: boolean; - private zoom_y: boolean; - private zoom_x_btn: HTMLLinkElement; - private zoom_y_btn: HTMLLinkElement; private range_x: jquery.flot.range; - private range_y: jquery.flot.range; - - constructor(document: Document, - private plot_placeholder: JQuery, - private driver: PulseGenerator) { - this.setPlot(); - this.range_x = {}; - this.range_x.from = 0; - this.range_x.to = this.plot_x_max; - - this.range_y = {}; - this.range_y.from = this.min_y; - this.range_y.to = this.max_y; - - this.update_plot(); - } - - update_plot() { - this.driver.getFifoBuffer( (adc0: number[][], adc1: number[][]) => { - this.redraw(adc0, adc1, () => { - requestAnimationFrame( () => { this.update_plot(); } ); + constructor(document: Document, private driver, private plotBasics: PlotBasics) { + // this.rangeFunction = "setTimeRange"; + this.updatePlot(); + this.range_x = { + "from": 0, + "to": 1024 + }; + } + + updatePlot() { + this.driver.getFifoBuffer( (ch0: number[][], ch1: number[][]) => { + this.plotBasics.redrawTwoChannels(ch0, ch1, this.range_x, "Channel 1", "Channel 2", true, true, () => { + requestAnimationFrame( () => { + this.updatePlot(); + }); }); }); } - setPlot() { - this.reset_range = false; - - this.options = { - series: { - shadowSize: 0 // Drawing is faster without shadows - }, - yaxis: { - min: this.min_y, - max: this.max_y - }, - xaxis: { - min: 0, - max: this.plot_x_max, - show: true - }, - grid: { - margin: { - left: 15 - } - }, - selection: { - mode: 'xy' - }, - colors: ['#0022FF', '#006400'], - legend: { - show: true, - noColumns: 0, - labelFormatter: (label: string, series: any): string => {return '' + label + '\t'}, - margin: 0, - position: 'ne', - } - } - - this.rangeSelect(); - this.dblClick(); - this.onWheel(); - this.reset_range = true; - } - - rangeSelect() { - this.plot_placeholder.bind('plotselected', (event: JQueryEventObject, ranges: jquery.flot.ranges) => { - // Clamp the zooming to prevent external zoom - if (ranges.xaxis.to - ranges.xaxis.from < 0.00001) { - ranges.xaxis.to = ranges.xaxis.from + 0.00001; - } - - if (ranges.yaxis.to - ranges.yaxis.from < 0.00001) { - ranges.yaxis.to = ranges.yaxis.from + 0.00001; - } - - this.range_x.from = ranges.xaxis.from; - this.range_x.to = ranges.xaxis.to; - - this.range_y.from = ranges.yaxis.from; - this.range_y.to = ranges.yaxis.to; - - this.resetRange(); - }); - } - - // A double click on the plot resets to full span - dblClick() { - this.plot_placeholder.bind('dblclick', (evt: JQueryEventObject) => { - this.range_x.from = 0; - this.range_x.to = this.plot_x_max; - - this.range_y = {}; - this.resetRange(); - }); - } - - onWheel() { - this.plot_placeholder.bind('wheel', (evt: JQueryEventObject) => { - let delta: number = (evt.originalEvent).deltaX - + (evt.originalEvent).deltaY; - delta /= Math.abs(delta); - - const zoom_ratio: number = 0.2; - - if ((evt.originalEvent).shiftKey) { // Zoom Y - const position_y_px: number = (evt.originalEvent).pageY - this.plot.offset().top; - const y0: any = this.plot.getAxes().yaxis.c2p(position_y_px); - - this.range_y.from = y0 - (1 + zoom_ratio * delta) * (y0 - this.plot.getAxes().yaxis.min); - this.range_y.to = y0 - (1 + zoom_ratio * delta) * (y0 - this.plot.getAxes().yaxis.max); - - this.resetRange(); - return false; - } else if ((evt.originalEvent).altKey) { // Zoom X - const position_x_px: number = (evt.originalEvent).pageX - this.plot.offset().left; - const t0: any = this.plot.getAxes().xaxis.c2p(position_x_px); - - if (t0 < 0 || t0 > this.plot_x_max) { - return; - } - - this.range_x.from = Math.max(t0 - (1 + zoom_ratio * delta) * (t0 - this.plot.getAxes().xaxis.min), 0); - this.range_x.to = Math.min(t0 - (1 + zoom_ratio * delta) * (t0 - this.plot.getAxes().xaxis.max), this.plot_x_max); - - this.resetRange(); - return false; - } - }); - } - - autoscale() { - this.range_x.from = 0; - this.range_x.to = this.plot_x_max; - - this.range_y = {}; - this.resetRange(); - } - - resetRange() { - this.reset_range = true; - } - - redraw(channel0: number[][], channel1: number[][], callback: () => void) { - if (channel0.length === 0 || channel1.length === 0) { - callback(); - return; - } - - const plt_data: jquery.flot.dataSeries[] - = [{label: 'ADC 1', data: channel0}, - {label: 'ADC 2', data: channel1}]; - - if (this.reset_range) { - this.plot_x_max = channel0.length; - - this.options.xaxis.min = this.range_x.from; - this.options.xaxis.max = this.range_x.to; - this.options.yaxis.min = this.range_y.from; - this.options.yaxis.max = this.range_y.to; - - this.plot = $.plot(this.plot_placeholder, plt_data, this.options); - - this.plot.setupGrid(); - this.reset_range = false; - } else { - this.plot.setData(plt_data); - this.plot.draw(); - } - - callback(); - } } \ No newline at end of file diff --git a/examples/red-pitaya/spectrum/config.yml b/examples/red-pitaya/spectrum/config.yml index 1813472e2..b11bb90c2 100644 --- a/examples/red-pitaya/spectrum/config.yml +++ b/examples/red-pitaya/spectrum/config.yml @@ -1,7 +1,7 @@ --- name: spectrum board: boards/red-pitaya -version: 0.1.0 +version: 0.1.1 memory: - name: control @@ -122,11 +122,15 @@ web: - web/koheron.ts - web/jquery.flot.d.ts - web/laser.ts + - web/laser-control.html - ./web/spectrum.ts - ./web/app.ts - web/main.css - web/average.ts + - web/average.html - web/modulation.ts + - web/modulation.html - ./web/plot.ts - - ./web/index.html - - web/navigation.ts \ No newline at end of file + - web/plot-basics/plot-basics.ts + - web/plot-basics/plot-basics.html + - ./web/index.html \ No newline at end of file diff --git a/examples/red-pitaya/spectrum/web/app.ts b/examples/red-pitaya/spectrum/web/app.ts index aa64b9fd3..53532654f 100644 --- a/examples/red-pitaya/spectrum/web/app.ts +++ b/examples/red-pitaya/spectrum/web/app.ts @@ -7,25 +7,40 @@ class App { public modulationControl: ModulationControl; public average: Average; public plot: Plot; - private navigation: Navigation; + private imports: Imports; + private plotBasics: PlotBasics; wfmSize = 8192; samplingRate = 125e6; + private n_pts: number; + private x_min: number; + private x_max: number; + private y_min: number; + private y_max: number; + constructor(window: Window, document: Document, ip: string, plot_placeholder: JQuery) { let client = new Client(ip, 5); - window.addEventListener('load', () => { + window.addEventListener('HTMLImportsLoaded', () => { client.init( () => { + this.imports = new Imports(document); this.laserDriver = new LaserDriver(client); this.laserControl = new LaserControl(document, this.laserDriver); this.spectrum = new Spectrum(client); this.average = new Average(document, this.spectrum); this.modulationDriver = new ModulationDriver(client); this.modulationControl = new ModulationControl(document, this.modulationDriver, this.wfmSize, this.samplingRate); - this.plot = new Plot(document, plot_placeholder, this.spectrum); - this.navigation = new Navigation(document); + + this.n_pts = 8192; + this.x_min = 0; + this.x_max = 62.5; + this.y_min = 0; + this.y_max = 200; + + this.plotBasics = new PlotBasics(document, plot_placeholder, this.plot, this.n_pts, this.x_min, this.x_max, this.y_min, this.y_max, this.spectrum, "setFreqRange", "Frequency (MHz)"); + this.plot = new Plot(document, this.spectrum, this.plotBasics); }); }, false); diff --git a/examples/red-pitaya/spectrum/web/index.html b/examples/red-pitaya/spectrum/web/index.html index 192245b2f..32a867386 100644 --- a/examples/red-pitaya/spectrum/web/index.html +++ b/examples/red-pitaya/spectrum/web/index.html @@ -12,6 +12,8 @@ + + @@ -20,21 +22,17 @@ + + + + + + - +
@@ -42,251 +40,24 @@
-
Frequency (MHz)
- - +
-
- -
- Laser -
- - - - - - - -
- - - - - - -
- - - - - - - - - - - - - - - - - - -
- - - - - -
- - - -
Power (µW) - - - -
- -
+
+
+
- -
- Average -
- - - - - - - - - -
- - - Count: - - Minimum: - -
- -
- -
- -
- Modulation -
- -
- -
- 1 -
-
- 2 -
-
- -
-
- -
- Sine -
-
- Triangle -
-
- Square -
-
- - - - - - - - - - - - - - - - - -
Amplitude (arb. units) - - - -
Frequency (MHz) - - - -
Offset (arb. units) - - - -
-
- - - -
- -
-
Velocity
- -
diff --git a/examples/red-pitaya/spectrum/web/plot.ts b/examples/red-pitaya/spectrum/web/plot.ts index 8091d05de..225f07e8b 100644 --- a/examples/red-pitaya/spectrum/web/plot.ts +++ b/examples/red-pitaya/spectrum/web/plot.ts @@ -3,56 +3,36 @@ class Plot { - private plot: jquery.flot.plot; - private options: jquery.flot.plotOptions; - private isResetRange: boolean; - - private minY: number; - private maxY: number; - private rangeY: jquery.flot.range; - - private xLabel: string; - private yLabel: string; - private xLabelDiv: HTMLDivElement; + private xlabel: string; + private ylabel: string; + private xLabelSpan: HTMLSpanElement; + private velocitySwitch: HTMLInputElement; private isPlotVelocity: boolean; private velocity: Array; - constructor(document: Document, private placeholder: JQuery, private driver: Spectrum) { - - this.setPlot(); - - this.minY = 0; - this.maxY = 200; + private peakDatapoint: number[]; - this.rangeY = { - from: this.minY, - to: this.maxY - }; - - this.yLabel = "Power Spectral Density (dB)"; - this.xLabelDiv = document.getElementById("x-label"); - this.xLabel = "Frequency (MHz)"; + constructor(document: Document, private driver, private plotBasics: PlotBasics) { + this.ylabel = "Power Spectral Density (dB)"; + this.xLabelSpan = document.getElementById("plot-title"); + this.xlabel = "Frequency (MHz)"; this.isPlotVelocity = false; this.velocity = []; + this.velocitySwitch = document.getElementById("velocity-switch"); + this.initVelocitySwitch(); this.updatePlot(); } updatePlot(): void { - this.xLabelDiv.innerHTML = this.xLabel; - - var plotData: number[][] = []; - var plotRangeX: jquery.flot.range = { - from: 0, - to: 0 - }; + this.xLabelSpan.innerHTML = this.xlabel; if (this.isPlotVelocity) { this.driver.getPeakFifoData( (peakFifoData) => { var time: number[] = []; - var velocityData: number[][] = []; + var plot_data: number[][] = []; const samplingFrequency: number = 125e6; //Hz const fftSize: number = 4096; @@ -69,201 +49,45 @@ class Plot { time[i] = (i / samplingFrequency) * fftSize; } for (let i: number = 0; i < n; i ++) { - velocityData[i] = [time[i], this.velocity[i]]; + plot_data[i] = [time[i], this.velocity[i]]; } this.driver.setAddressRange(2, fftSize / 2); - plotData = velocityData; - plotRangeX = { + const range_x = { from: 0, to: 1 } - this.redraw(plotData, plotRangeX, () => { + this.plotBasics.redrawRange(plot_data, range_x, this.ylabel, () => { requestAnimationFrame( () => { this.updatePlot(); }); }); }); } else { - this.driver.getDecimatedData( (decimatedData: number[][], decimatedRangeX: jquery.flot.range) => { - - this.redraw(decimatedData, decimatedRangeX, () => { + this.driver.getDecimatedData( (plot_data: number[][], range_x: jquery.flot.range) => { + this.plotBasics.redrawRange(plot_data, range_x, this.ylabel, () => { requestAnimationFrame( () => { this.updatePlot(); }); }); }); } - - - } - - // == Plot - - setPlot(): void { - this.isResetRange = false; - - let labelAttribute: string = ""; - labelAttribute += " style='font-size: 16px; color: #333'"; - - this.options = { - series: { - shadowSize: 0 // Drawing is faster without shadows - }, - yaxis: { - min: this.minY, - max: this.maxY - }, - xaxis: { - min: 0, - max: 62.5, - show: true - }, - grid: { - margin: { - top: 0, - left: 0, - }, - borderColor: "#d5d5d5", - borderWidth: 1 - }, - selection: { - mode: 'xy' - }, - colors: ["#019cd5"], - legend: { - show: true, - noColumns: 0, - labelFormatter: (label: string, series: any): string => { - return "" + label + "\t" - }, - margin: 0, - position: "ne", - } - } - - this.rangeSelect(); - this.autoScale(); - this.onWheel(); - this.isResetRange = true; } - rangeSelect(): void { - this.placeholder.bind('plotselected', (event: JQueryEventObject, ranges: jquery.flot.ranges) => { - // Clamp the zooming to prevent external zoom - if (ranges.xaxis.to - ranges.xaxis.from < 0.00001) { - ranges.xaxis.to = ranges.xaxis.from + 0.00001; + initVelocitySwitch(): void { + this.velocitySwitch.addEventListener('change', (event) => { + if (this.isPlotVelocity) { + this.ylabel = "Power Spectral Density (dB)"; + this.xlabel = "Frequency (MHz)"; + this.isPlotVelocity = false; + // this.isResetRange = true; + } else { + this.ylabel = "Speed (m/s)"; + this.xlabel = "Time (s)"; + this.isPlotVelocity = true; + // this.isResetRange = true; } - if (ranges.yaxis.to - ranges.yaxis.from < 0.00001) { - ranges.yaxis.to = ranges.yaxis.from + 0.00001; - } - - this.rangeY = { - from: ranges.yaxis.from, - to: ranges.yaxis.to - }; - - this.driver.setFreqRange(ranges.xaxis); - this.resetRange(); - }); - } - - // A double click on the plot resets to full span - autoScale(): void { - this.placeholder.bind('dblclick', (evt: JQueryEventObject) => { - const rangeX: jquery.flot.range = { - from : 0, - to : 62.5 - }; - - this.rangeY = {}; - this.driver.setFreqRange(rangeX); - this.resetRange(); - }); - } - - onWheel(): void { - this.placeholder.bind('wheel', (evt: JQueryEventObject) => { - let delta: number = (evt.originalEvent).deltaX - + (evt.originalEvent).deltaY; - delta /= Math.abs(delta); - - const zoomRatio: number = 0.2; - - if ((evt.originalEvent).shiftKey) { // Zoom Y - const positionY: number = (evt.originalEvent).pageY - this.plot.offset().top; - const y0: any = this.plot.getAxes().yaxis.c2p(positionY); - - this.rangeY = { - from: y0 - (1 + zoomRatio * delta) * (y0 - this.plot.getAxes().yaxis.min), - to: y0 - (1 + zoomRatio * delta) * (y0 - this.plot.getAxes().yaxis.max) - }; - - this.resetRange(); - return false; - } else if ((evt.originalEvent).altKey) { // Zoom X - const positionX: number = (evt.originalEvent).pageX - this.plot.offset().left; - const freq0: any = this.plot.getAxes().xaxis.c2p(positionX); - - if (freq0 < 0 || freq0 > 62.5) { - return; - } - - const rangeX: jquery.flot.range = { - from: Math.max(freq0 - (1 + zoomRatio * delta) * (freq0 - this.plot.getAxes().xaxis.min), 0), - to: Math.min(freq0 - (1 + zoomRatio * delta) * (freq0 - this.plot.getAxes().xaxis.max), 62.5) - }; - - this.driver.setFreqRange(rangeX); - this.resetRange(); - return false; - } - - return true - }); - } - - resetRange(): void { - this.isResetRange = true; - } - - redraw(data: number[][], rangeX: jquery.flot.range, callback: () => void): void { - - const plt_data: jquery.flot.dataSeries[] = [{label: this.yLabel, data: data}]; - - if (data.length == 0) { - callback(); - return; - } - - if (this.isResetRange) { - this.options.xaxis.min = rangeX.from; - this.options.xaxis.max = rangeX.to; - this.options.yaxis.min = this.rangeY.from; - this.options.yaxis.max = this.rangeY.to; - this.plot = $.plot(this.placeholder, plt_data, this.options); - this.plot.setupGrid(); - this.isResetRange = false; - } else { - this.plot.setData(plt_data); - this.plot.draw(); - } - - callback(); - } - - switchVelocity(): void { - if (this.isPlotVelocity) { - this.yLabel = "Power Spectral Density (dB)"; - this.xLabel = "Frequency (MHz)"; - this.isPlotVelocity = false; - this.isResetRange = true; - } else { - this.yLabel = "Speed (m/s)"; - this.xLabel = "Time (s)"; - this.isPlotVelocity = true; - this.isResetRange = true; - } + }) } rollArray(array, count) { diff --git a/examples/te0745/default/config.yml b/examples/te0745/default/config.yml index 86e2ad691..52378c2f6 100644 --- a/examples/te0745/default/config.yml +++ b/examples/te0745/default/config.yml @@ -39,5 +39,4 @@ web: - web/koheron.ts - ./web/monitor.ts - ./web/app.ts - - web/main.css - - web/navigation.ts + - web/main.css \ No newline at end of file diff --git a/examples/te0745/default/web/app.ts b/examples/te0745/default/web/app.ts index 8a53ee982..10fe88ec4 100644 --- a/examples/te0745/default/web/app.ts +++ b/examples/te0745/default/web/app.ts @@ -1,8 +1,6 @@ class App { private monitor: Monitor; - private navigation: Navigation; - private temperatureSpan: HTMLSpanElement; constructor(window: Window, document: Document, ip: string) { @@ -12,7 +10,6 @@ class App { window.addEventListener('load', () => { client.init( () => { this.monitor = new Monitor(client); - this.navigation = new Navigation(document); this.update(); }); }, false); diff --git a/examples/te0745/default/web/index.html b/examples/te0745/default/web/index.html index fa0f9dcbe..4e15dda96 100644 --- a/examples/te0745/default/web/index.html +++ b/examples/te0745/default/web/index.html @@ -21,22 +21,11 @@ - -
Temperature (°C): +

This instrument was built with Koheron Software Development Kit. See settings.
diff --git a/examples/zc706/led-blinker/config.yml b/examples/zc706/led-blinker/config.yml index 950d80293..3e4ef207f 100644 --- a/examples/zc706/led-blinker/config.yml +++ b/examples/zc706/led-blinker/config.yml @@ -34,7 +34,7 @@ drivers: web: - ./web/index.html - web/koheron.ts - - ./web/led_blinker.ts + - web/led-blinker.ts - ./web/app.ts - ./web/control.ts - ./web/main.css \ No newline at end of file diff --git a/examples/zc706/led-blinker/web/app.ts b/examples/zc706/led-blinker/web/app.ts index cb8ae5f5f..c933efbbf 100644 --- a/examples/zc706/led-blinker/web/app.ts +++ b/examples/zc706/led-blinker/web/app.ts @@ -1,6 +1,6 @@ class App { private driver: LedBlinker; - public control: Control; + public control: LedBlinkerControl; constructor(window: Window, document: Document, ip: string, plot_placeholder: JQuery) { @@ -9,7 +9,7 @@ class App { window.addEventListener('load', () => { client.init( () => { this.driver = new LedBlinker(client); - this.control = new Control(document, this.driver); + this.control = new LedBlinkerControl(document, this.driver, 4); }); }, false); diff --git a/examples/zc706/led-blinker/web/control.ts b/examples/zc706/led-blinker/web/control.ts deleted file mode 100644 index baf82b9fb..000000000 --- a/examples/zc706/led-blinker/web/control.ts +++ /dev/null @@ -1,41 +0,0 @@ -// LED Blinker -// (c) Koheron - -class Control { - - private ledBtns: HTMLInputElement[]; - private ledStatus: boolean[]; - - constructor(private document: Document, private driver: LedBlinker) { - - this.ledBtns = []; - this.ledStatus = []; - - for (let i: number = 0; i < 4 ; i++) { - this.ledBtns[i] = document.getElementById('led-' + i.toString()); - } - - this.update(); - - } - - update(): void { - this.driver.getLeds( (value) => { - for (let i: number = 0; i < 4 ; i++) { - this.ledStatus[i] = (((value >> i) % 2) === 1); - if (this.ledStatus[i]) { - this.ledBtns[i].value = 'OFF'; - } - else { - this.ledBtns[i].value = 'ON'; - } - }; - requestAnimationFrame( () => { this.update(); } ) - }); - } - - switchLed(index: number) { - this.driver.setLed(index, !this.ledStatus[index]); - } - -} diff --git a/examples/zc706/led-blinker/web/index.html b/examples/zc706/led-blinker/web/index.html index 4b7bd781c..ed3ac75ab 100644 --- a/examples/zc706/led-blinker/web/index.html +++ b/examples/zc706/led-blinker/web/index.html @@ -2,7 +2,7 @@ - Koheron + Koheron | LED Blinker @@ -21,42 +21,10 @@ -
- - Koheron - -
-
-

ZC706 LED blinker

- - - - - - - - - - - - - - - - - - -
LEDs0123
ON/OFF
- -

- -

- See available instruments. -

- This instrument was built with Koheron Software Development Kit.
-

+
+

This instrument was built with Koheron Software Development Kit. See settings.
diff --git a/examples/zc706/led-blinker/web/led_blinker.ts b/examples/zc706/led-blinker/web/led_blinker.ts deleted file mode 100644 index 9ce4f0224..000000000 --- a/examples/zc706/led-blinker/web/led_blinker.ts +++ /dev/null @@ -1,21 +0,0 @@ -class LedBlinker { - private driver: Driver; - private id: number; - private cmds: HashTable; - - constructor (private client: Client) { - this.driver = this.client.getDriver('LedBlinker'); - this.id = this.driver.id; - this.cmds = this.driver.getCmds(); - } - - getLeds(cb: (value: number) => void): void { - this.client.readUint32(Command(this.id, this.cmds['get_leds']), - (value) => {cb(value)}); - } - - setLed(index:number, status: boolean): void { - this.client.send(Command(this.id, this.cmds['set_led'], index, status)); - } - -} \ No newline at end of file diff --git a/examples/zedboard/led-blinker/config.yml b/examples/zedboard/led-blinker/config.yml index d4356474e..2770a2732 100644 --- a/examples/zedboard/led-blinker/config.yml +++ b/examples/zedboard/led-blinker/config.yml @@ -1,7 +1,7 @@ --- name: led-blinker board: boards/zedboard -version: 0.1.0 +version: 0.1.1 cores: - fpga/cores/axi_ctl_register_v1_0 @@ -35,8 +35,7 @@ drivers: web: - ./web/index.html - web/koheron.ts - - ./web/led_blinker.ts + - web/led-blinker.ts - ./web/app.ts - ./web/control.ts - - web/main.css - - web/navigation.ts \ No newline at end of file + - web/main.css \ No newline at end of file diff --git a/examples/zedboard/led-blinker/web/app.ts b/examples/zedboard/led-blinker/web/app.ts index cf449d1b0..52c20cebd 100644 --- a/examples/zedboard/led-blinker/web/app.ts +++ b/examples/zedboard/led-blinker/web/app.ts @@ -1,17 +1,17 @@ class App { private driver: LedBlinker; - public control: Control; - private navigation: Navigation; + public control: LedBlinkerControl; + private imports: Imports; constructor(window: Window, document: Document, - ip: string, plot_placeholder: JQuery) { + ip: string) { let client = new Client(ip, 5); - window.addEventListener('load', () => { + window.addEventListener('HTMLImportsLoaded', () => { client.init( () => { + this.imports = new Imports(document); this.driver = new LedBlinker(client); - this.control = new Control(document, this.driver); - this.navigation = new Navigation(document); + this.control = new LedBlinkerControl(document, this.driver, 8); }); }, false); @@ -19,4 +19,4 @@ class App { } } -let app = new App(window, document, location.hostname, $('#plot-placeholder')); \ No newline at end of file +let app = new App(window, document, location.hostname); \ No newline at end of file diff --git a/examples/zedboard/led-blinker/web/index.html b/examples/zedboard/led-blinker/web/index.html index a1a63ec33..c2c1c8a9a 100644 --- a/examples/zedboard/led-blinker/web/index.html +++ b/examples/zedboard/led-blinker/web/index.html @@ -15,70 +15,24 @@ + + + - +
-
- - - - - - - - - - - - - - - - - - - - - - - - - - -
LEDs01234567
ON/OFF
- +
+
- - diff --git a/examples/zedboard/led-blinker/web/led_blinker.ts b/examples/zedboard/led-blinker/web/led_blinker.ts deleted file mode 100644 index 9ce4f0224..000000000 --- a/examples/zedboard/led-blinker/web/led_blinker.ts +++ /dev/null @@ -1,21 +0,0 @@ -class LedBlinker { - private driver: Driver; - private id: number; - private cmds: HashTable; - - constructor (private client: Client) { - this.driver = this.client.getDriver('LedBlinker'); - this.id = this.driver.id; - this.cmds = this.driver.getCmds(); - } - - getLeds(cb: (value: number) => void): void { - this.client.readUint32(Command(this.id, this.cmds['get_leds']), - (value) => {cb(value)}); - } - - setLed(index:number, status: boolean): void { - this.client.send(Command(this.id, this.cmds['set_led'], index, status)); - } - -} \ No newline at end of file diff --git a/examples/zedboard/picoblaze/config.yml b/examples/zedboard/picoblaze/config.yml index 617003c2e..11ea43d24 100644 --- a/examples/zedboard/picoblaze/config.yml +++ b/examples/zedboard/picoblaze/config.yml @@ -1,7 +1,7 @@ --- name: picoblaze board: boards/zedboard -version: 0.1.0 +version: 0.1.1 cores: - fpga/cores/axi_ctl_register_v1_0 @@ -41,4 +41,4 @@ drivers: web: - web/index.html - web/main.css - - web/navigation.ts \ No newline at end of file + - web/koheron.ts \ No newline at end of file diff --git a/os/os.mk b/os/os.mk index 5053f4f79..e63887516 100644 --- a/os/os.mk +++ b/os/os.mk @@ -213,7 +213,10 @@ www : $(TMP_WWW_PATH)/koheron.css \ $(TMP_WWW_PATH)/lato-v11-latin-400.woff2 \ $(TMP_WWW_PATH)/lato-v11-latin-700.woff2 \ $(TMP_WWW_PATH)/lato-v11-latin-900.woff2 \ - $(TMP_WWW_PATH)/glyphicons-halflings-regular.woff2 + $(TMP_WWW_PATH)/glyphicons-halflings-regular.woff2 \ + $(TMP_WWW_PATH)/navigation.html \ + $(TMP_WWW_PATH)/html-imports.min.js \ + $(TMP_WWW_PATH)/html-imports.min.js.map .PHONY: www_sync www_sync: www @@ -226,7 +229,6 @@ clean_www: WWW_TS_FILES := $(WEB_PATH)/koheron.ts WWW_TS_FILES += $(WWW_PATH)/instruments.ts WWW_TS_FILES += $(WWW_PATH)/instruments_widget.ts -WWW_TS_FILES += $(WEB_PATH)/navigation.ts $(TMP_WWW_PATH)/instruments.js: $(WWW_TS_FILES) mkdir -p $(@D) @@ -240,6 +242,10 @@ $(TMP_WWW_PATH)/index.html: $(WWW_PATH)/index.html mkdir -p $(@D) cp $< $@ +$(TMP_WWW_PATH)/navigation.html: $(WEB_PATH)/navigation.html + mkdir -p $(@D) + cp $< $@ + $(TMP_WWW_PATH)/main.css: $(WEB_PATH)/main.css mkdir -p $(@D) cp $< $@ @@ -283,3 +289,11 @@ $(TMP_WWW_PATH)/lato-v11-latin-900.woff2: $(TMP_WWW_PATH)/glyphicons-halflings-regular.woff2: mkdir -p $(@D) curl https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/fonts/glyphicons-halflings-regular.woff2 -o $@ + +$(TMP_WWW_PATH)/html-imports.min.js: + mkdir -p $(@D) + curl https://raw.githubusercontent.com/webcomponents/html-imports/master/html-imports.min.js -o $@ + +$(TMP_WWW_PATH)/html-imports.min.js.map: + mkdir -p $(@D) + curl https://raw.githubusercontent.com/webcomponents/html-imports/master/html-imports.min.js.map -o $@ \ No newline at end of file diff --git a/os/www/index.html b/os/www/index.html index 4e97e55cf..d45bbcdcf 100644 --- a/os/www/index.html +++ b/os/www/index.html @@ -16,23 +16,16 @@ + + + - +
@@ -56,13 +49,11 @@

Instruments

diff --git a/version b/version index c5523bd09..66333910a 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.17.0 +0.18.0 diff --git a/web/average.html b/web/average.html new file mode 100644 index 000000000..d69f7392b --- /dev/null +++ b/web/average.html @@ -0,0 +1,22 @@ + \ No newline at end of file diff --git a/web/average.ts b/web/average.ts index ed4f5415f..b56108720 100644 --- a/web/average.ts +++ b/web/average.ts @@ -10,17 +10,21 @@ interface AverageStatus { class Average { private averageSwitch: HTMLInputElement; + private averageMinInput: HTMLInputElement; private averageElements: any; private averageSpan: HTMLSpanElement; - private numAverageMinInput: HTMLInputElement; private isAverage: boolean = false; constructor(document: Document, private driver: any) { this.averageSwitch = document.getElementById("average-switch"); + this.initAverageSwitch(); + + this.averageMinInput = document.getElementById("average-min-input"); + this.initAverageMinInput(); + this.averageElements = document.getElementsByClassName("average"); - this.averageSpan = document.getElementById('avg'); - this.numAverageMinInput = document.getElementById('avg-min-input'); + this.averageSpan = document.getElementById('average-value'); this.update(); } @@ -45,21 +49,20 @@ class Average { }); } - average(): void { - this.driver.setAverage(!this.isAverage); + initAverageSwitch(): void { + this.averageSwitch.addEventListener('change', (event) => { + this.driver.setAverage(!this.isAverage); + }) } - setNumAverageMin(numAverageMin) : void { - if (this.isAverage) { - this.driver.setNumAverageMin(Math.max(0, numAverageMin)); - } - } - - saveNumAverageMin(): void { - if (this.isAverage) { - this.driver.setNumAverageMin(Math.max(0, parseInt(this.numAverageMinInput.value))); + initAverageMinInput(): void { + let events = ['change', 'input']; + for (let event_ of events) { + this.averageMinInput.addEventListener(event_, (event) => { + if (this.isAverage) { + this.driver.setNumAverageMin(Math.max(0, parseInt((event.currentTarget).value))); + } + }) } - this.numAverageMinInput.style.display = 'none'; } - } \ No newline at end of file diff --git a/web/dds-frequency/dds-frequency.html b/web/dds-frequency/dds-frequency.html new file mode 100644 index 000000000..692c79efc --- /dev/null +++ b/web/dds-frequency/dds-frequency.html @@ -0,0 +1,29 @@ + \ No newline at end of file diff --git a/web/dds-frequency/dds-frequency.ts b/web/dds-frequency/dds-frequency.ts new file mode 100644 index 000000000..48b83b379 --- /dev/null +++ b/web/dds-frequency/dds-frequency.ts @@ -0,0 +1,31 @@ +// DDS Frequency wiget +// (c) Koheron + +class DDSFrequency { + private ddsChannelInputs: HTMLInputElement[]; + + constructor(document, private driver) { + this.ddsChannelInputs = document.getElementsByClassName("dds-channel-input"); + this.initDDSChannelInputs(); + } + + initDDSChannelInputs(): void { + let events = ['change', 'input']; + for (let j = 0; j < events.length; j++) { + for (let i = 0; i < this.ddsChannelInputs.length; i++) { + this.ddsChannelInputs[i].addEventListener(events[j], (event) => { + let counterType: string = "number"; + if ((event.currentTarget).type == "number") { + counterType = "range"; + } + let command = (event.currentTarget).dataset.command; + let channel = (event.currentTarget).dataset.channel; + let value = (event.currentTarget).value; + (document.querySelector("[data-command='" + command + "'][data-channel='" + channel +"'][type='" + counterType + "']")).value = value ; + this.driver[command](channel, 1e6 * parseFloat(value)); + }) + } + } + } + +} \ No newline at end of file diff --git a/web/downloads.mk b/web/downloads.mk index ce1fbab3c..5fdc6611c 100644 --- a/web/downloads.mk +++ b/web/downloads.mk @@ -16,6 +16,9 @@ WEB_DOWNLOADS += $(TMP_WEB_PATH)/lato-v11-latin-400.woff2 WEB_DOWNLOADS += $(TMP_WEB_PATH)/lato-v11-latin-700.woff2 WEB_DOWNLOADS += $(TMP_WEB_PATH)/lato-v11-latin-900.woff2 WEB_DOWNLOADS += $(TMP_WEB_PATH)/glyphicons-halflings-regular.woff2 +WEB_DOWNLOADS += $(TMP_WEB_PATH)/html-imports.min.js +WEB_DOWNLOADS += $(TMP_WEB_PATH)/html-imports.min.js.map +WEB_DOWNLOADS += $(TMP_WEB_PATH)/navigation.html $(TMP_WEB_PATH)/_koheron.css: mkdir -p $(@D) @@ -83,4 +86,16 @@ $(TMP_WEB_PATH)/lato-v11-latin-900.woff2: $(TMP_WEB_PATH)/glyphicons-halflings-regular.woff2: mkdir -p $(@D) - curl https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/fonts/glyphicons-halflings-regular.woff2 -o $@ \ No newline at end of file + curl https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/fonts/glyphicons-halflings-regular.woff2 -o $@ + +$(TMP_WEB_PATH)/html-imports.min.js: + mkdir -p $(@D) + curl https://raw.githubusercontent.com/webcomponents/html-imports/master/html-imports.min.js -o $@ + +$(TMP_WEB_PATH)/html-imports.min.js.map: + mkdir -p $(@D) + curl https://raw.githubusercontent.com/webcomponents/html-imports/master/html-imports.min.js.map -o $@ + +$(TMP_WEB_PATH)/navigation.html: $(WEB_PATH)/navigation.html + mkdir -p $(@D) + cp $< $@ \ No newline at end of file diff --git a/web/index.html b/web/index.html index 431a8cffd..849769250 100644 --- a/web/index.html +++ b/web/index.html @@ -15,49 +15,31 @@ + + + - - -
- -
- -

Bonjour!

- -

- -

There is no web interface for this instrument. - You can write your own interface using the TypeScript API - or the Python API.

-

- -
+
+

Bonjour!

+

There is no web interface for this instrument. + You can write your own interface using the TypeScript API + or the Python API.

+

- diff --git a/web/koheron.ts b/web/koheron.ts index f4fc6a201..43bd7ba8d 100644 --- a/web/koheron.ts +++ b/web/koheron.ts @@ -676,4 +676,18 @@ function __range__(left, right, inclusive) { range.push(i); } return range; -} \ No newline at end of file +} + +class Imports { + private importLinks: HTMLLinkElement[]; + + constructor (document: Document) { + this.importLinks = document.querySelectorAll('link[rel="import"]'); + for (let i = 0; i < this.importLinks.length ; i++) { + let template = this.importLinks[i].import.querySelector('.template'); + let clone = document.importNode(template.content, true); + let parentId = this.importLinks[i].dataset.parent; + document.getElementById(parentId).appendChild(clone); + } + } + } diff --git a/web/laser-control.html b/web/laser-control.html new file mode 100644 index 000000000..9c74d96a2 --- /dev/null +++ b/web/laser-control.html @@ -0,0 +1,43 @@ + \ No newline at end of file diff --git a/web/laser.ts b/web/laser.ts index 18d52574e..6d5c53073 100644 --- a/web/laser.ts +++ b/web/laser.ts @@ -73,34 +73,31 @@ class LaserControl { private laserModeSelect: HTMLSelectElement; private calibrationSpan: HTMLSpanElement; - private currentControl: any; - private inCurrentSlider: HTMLInputElement; - private inCurrentInput: HTMLInputElement; - private powerControl: any; - private inPowerInput: HTMLInputElement; - private inPowerSlider: HTMLInputElement; private measuredPowerSpan: HTMLSpanElement; private canvas: HTMLCanvasElement; private calibrationInstructionsDiv: any; private ctx: any; + private laserControlInputs: HTMLInputElement[]; + constructor(private document: Document, private driver: LaserDriver) { this.laserSwitch = document.getElementById('laser-switch'); - this.laserModeSelect = document.getElementById('laser-mode'); - this.calibrationSpan = document.getElementById('calibration'); - this.currentControl = document.getElementsByClassName('current-control'); - this.inCurrentSlider = document.getElementById('in-current-slider'); - this.inCurrentInput = document.getElementById('in-current-input'); - this.powerControl = document.getElementsByClassName('power-control'); - this.inPowerSlider = document.getElementById('in-power-slider'); - this.inPowerInput = document.getElementById('in-power-input'); - this.measuredPowerSpan = document.getElementById('measured-power'); - this.canvas = document.getElementById('canvas'); + this.initLaserSwitch(); + + this.calibrationSpan = document.getElementById('calibration'); this.calibrationInstructionsDiv = document.getElementById('calibration-instructions'); + this.initStartCalibration(); + this.initEndCalibration(); + + this.laserModeSelect = document.getElementById('laser-mode'); + this.initLaserModeSelect(); - let canvasWidth: number = this.inCurrentSlider.offsetWidth; - this.canvas.width = canvasWidth; + this.laserControlInputs = document.getElementsByClassName("laser-control-input"); + this.initLaserControlInputs(); + this.measuredPowerSpan = document.getElementById('measured-power'); + this.canvas = document.getElementById('canvas'); + this.canvas.width = (document.querySelector(".laser-control-input[type='range']")).offsetWidth; this.ctx = this.canvas.getContext("2d"); this.ctx.fillStyle = 'rgb(100, 100, 100)'; @@ -110,7 +107,7 @@ class LaserControl { update(): void { this.driver.getStatus ( (status) => { - this.measuredPowerSpan.innerHTML = Math.max(0, status.measured_power).toFixed(1).toString(); + this.measuredPowerSpan.innerHTML = Math.max(0, status.measured_power).toFixed(1); this.ctx.clearRect(0, 0, this.canvas.width, 15); this.ctx.fillRect(0, 0, status.measured_power * this.canvas.width / 4000, 15); @@ -120,82 +117,97 @@ class LaserControl { this.calibrationSpan.innerHTML = 'Status: Not calibrated'; } - if (document.activeElement !== this.inCurrentInput) { - this.inCurrentInput.value = status.current.toFixed(2).toString(); - } - if (document.activeElement !== this.inCurrentSlider) { - this.inCurrentSlider.value = status.current.toFixed(2).toString(); + let laserControlInputsArray = []; + for (let j = 0; j < this.laserControlInputs.length; j++) { + laserControlInputsArray.push(this.laserControlInputs[j]); } - if (document.activeElement !== this.inPowerInput) { - this.inPowerInput.value = status.power.toFixed(1).toString(); - } - if (document.activeElement !== this.inPowerSlider) { - this.inPowerSlider.value = status.power.toFixed(1).toString(); + if (laserControlInputsArray.indexOf(document.activeElement) == -1) { + for (let j = 0; j < this.laserControlInputs.length; j++) { + if (this.laserControlInputs[j].dataset.command === "setCurrent") { + this.laserControlInputs[j].value = status.current.toFixed(2); + } else if (this.laserControlInputs[j].dataset.command === "setPower") { + this.laserControlInputs[j].value = status.power.toFixed(1); + } + } } this.laserOn = status.laser_on; - if (this.laserOn) { - this.laserSwitch.checked = true; - } else { - this.laserSwitch.checked = false; - } + this.laserSwitch.checked = this.laserOn; if (status.constant_power_on) { this.laserModeSelect.value = "power"; - for (let i: number = 0; i < this.currentControl.length; i++) { - this.currentControl[i].style.display = 'none'; - } - for (let i: number = 0; i < this.powerControl.length; i++) { - this.powerControl[i].style.display = 'table-cell'; + for (let i: number = 0; i < this.laserControlInputs.length; i++) { + this.laserControlInputs[i].dataset.command = "setPower"; + this.laserControlInputs[i].max = "4000"; } } else { this.laserModeSelect.value = 'current'; - for (let i: number = 0; i < this.currentControl.length; i++) { - this.currentControl[i].style.display = 'table-cell'; - } - for (let i: number = 0; i < this.powerControl.length; i++) { - this.powerControl[i].style.display = 'none'; + for (let i: number = 0; i < this.laserControlInputs.length; i++) { + this.laserControlInputs[i].dataset.command = "setCurrent"; + this.laserControlInputs[i].max = "40"; } - } requestAnimationFrame( () => { this.update(); }); }); } - switchLaser(): void { - if (this.laserOn) { - this.driver.stop(); - this.laserOn = false; - } else { - this.driver.start(); - this.laserOn = true; + initLaserSwitch(): void { + this.laserSwitch.addEventListener('change', (event) => { + if (this.laserOn) { + this.driver.stop(); + this.laserOn = false; + } else { + this.driver.start(); + this.laserOn = true; + } + }) + } + + initLaserModeSelect(): void { + this.laserModeSelect.addEventListener('change', (event) => { + this.driver.switchMode(); + }) + } + + initLaserControlInputs(): void { + let events = ['change', 'input']; + for (let j = 0; j < events.length; j++) { + for (let i = 0; i < this.laserControlInputs.length; i++) { + this.laserControlInputs[i].addEventListener(events[j], (event) => { + let counterType: string = "number"; + if ((event.currentTarget).type == "number") { + counterType = "range"; + } + let command = (event.currentTarget).dataset.command; + let value = (event.currentTarget).value; + (document.querySelector(".laser-control-input[data-command='" + command + "'][type='" + counterType + "']")).value = value ; + this.driver[command](parseFloat(value)); + }) + } } - } - switchMode(): void { - this.driver.switchMode(); } - setCurrent(value: string): void { - this.driver.setCurrent(parseFloat(value)); + initStartCalibration(): void { + let startCalibrationBtn = document.getElementById("start-calibration-btn"); + startCalibrationBtn.addEventListener('click', (event) => { + this.driver.calibrate0mW(); + this.calibrationSpan.style.display = "none"; + this.calibrationInstructionsDiv.style.display = 'block'; + }) } - setPower(value: string): void { - this.driver.setPower(parseFloat(value)); + initEndCalibration(): void { + let endCalibrationBtn = document.getElementById("end-calibration-btn"); + endCalibrationBtn.addEventListener('click', (event) => { + this.driver.calibrate1mW(); + this.calibrationSpan.style.display = "inline"; + this.calibrationInstructionsDiv.style.display = 'none'; + }) } - startCalibration(): void { - this.driver.calibrate0mW(); - this.calibrationSpan.style.display = "none"; - this.calibrationInstructionsDiv.style.display = 'block'; - } - calibrationDone(): void { - this.driver.calibrate1mW(); - this.calibrationSpan.style.display = "inline"; - this.calibrationInstructionsDiv.style.display = 'none'; - } } \ No newline at end of file diff --git a/web/led-blinker.ts b/web/led-blinker.ts new file mode 100644 index 000000000..dc6051bc6 --- /dev/null +++ b/web/led-blinker.ts @@ -0,0 +1,106 @@ +// LED Blinker +// (c) Koheron + +class LedBlinker { + private driver: Driver; + private id: number; + private cmds: HashTable; + + constructor (private client: Client) { + this.driver = this.client.getDriver('LedBlinker'); + this.id = this.driver.id; + this.cmds = this.driver.getCmds(); + } + + getLeds(cb: (value: number) => void): void { + this.client.readUint32(Command(this.id, this.cmds['get_leds']), + (value) => {cb(value)}); + } + + setLed(index:number, status: boolean): void { + this.client.send(Command(this.id, this.cmds['set_led'], index, status)); + } + +} + +class LedBlinkerControl { + + private ledBtns: HTMLInputElement[]; + private ledStatus: boolean[]; + + constructor(private document: Document, private driver: LedBlinker, private ledCount: number) { + this.ledBtns = []; + this.ledBtns = document.getElementsByClassName("led-btn"); + this.ledStatus = []; + this.initLedTable(this.ledCount, () => { + this.initLedButtons(); + this.update(); + }) + } + + initLedTable(ledCount: number, callback): void { + + let ledTable = document.getElementById("leds-table"); + + let tr0 = document.createElement("tr"); + let th0 = document.createElement("th"); + th0.textContent = "LEDs"; + tr0.appendChild(th0); + for (let i = 0; i < ledCount; i++) { + let th = document.createElement("th"); + th.textContent = i.toString(); + th.style.width = "100px"; + th.style.textAlign = "center"; + tr0.appendChild(th); + } + + ledTable.appendChild(tr0); + + let tr1 = document.createElement("tr"); + let td0 = document.createElement("td"); + td0.textContent = "ON / OFF"; + tr1.appendChild(td0); + for (let i = 0; i < ledCount; i++) { + let td = document.createElement("td"); + td.style.width = "100 px"; + td.style.padding = "0px 5px"; + let input = document.createElement("input"); + input.type = "button"; + input.dataset.ledindex = i.toString(); + input.className = "led-btn"; + input.style.width = "100%"; + input.style.boxShadow = "0px 4px 6px 0px #e6e6e6"; + td.appendChild(input); + tr1.appendChild(td); + } + + ledTable.appendChild(tr1); + callback(); + + } + + initLedButtons(): void { + for (let i = 0; i < this.ledBtns.length; i++) { + this.ledBtns[i].addEventListener('click', (event) => { + let index: number = parseInt((event.currentTarget).dataset.ledindex); + console.log(index); + this.driver.setLed(index, !this.ledStatus[index]); + }) + } + } + + update(): void { + this.driver.getLeds( (value) => { + for (let i: number = 0; i < 8 ; i++) { + this.ledStatus[i] = (((value >> i) % 2) === 1); + if (this.ledStatus[i]) { + this.ledBtns[i].value = 'OFF'; + } + else { + this.ledBtns[i].value = 'ON'; + } + }; + requestAnimationFrame( () => { this.update(); } ) + }); + } +} diff --git a/web/main.css b/web/main.css index 68f0773a9..015457a2e 100644 --- a/web/main.css +++ b/web/main.css @@ -46,6 +46,8 @@ label { display: inline-block; width: 44px; height: 24px; + margin-bottom: -7px !important; + margin-left: 5px; } td > .switch { @@ -163,3 +165,9 @@ button[disabled] { margin-left: 100px; padding: 10px; } + +input[type="radio"] { + width: auto; + vertical-align: -12px; + margin: 0px 5px !important; +} diff --git a/web/modulation.html b/web/modulation.html new file mode 100644 index 000000000..f8f4c8a63 --- /dev/null +++ b/web/modulation.html @@ -0,0 +1,63 @@ + diff --git a/web/modulation.ts b/web/modulation.ts index e2b5732af..f8f652af9 100644 --- a/web/modulation.ts +++ b/web/modulation.ts @@ -65,99 +65,103 @@ class ModulationDriver { class ModulationControl { - private channelDivs: HTMLDivElement[]; - private waveformInputs: HTMLInputElement[][]; - private amplitudeSlider: HTMLInputElement[]; - private frequencySlider: HTMLInputElement[]; - private offsetSlider: HTMLInputElement[]; - private frequencySpan: HTMLSpanElement[]; - private amplitudeSpan: HTMLSpanElement[]; - private offsetSpan: HTMLSpanElement[]; - - // readonly waveformTypes = ['sine', 'triangle', 'square']; + private channelNum: number = 2; + private currentChannel: number; + private channelInputs: HTMLInputElement[]; + private channelElements: any[]; + private modulationInputs: HTMLInputElement[]; constructor(document: Document, private driver: ModulationDriver, private wfmSize: number, private samplingRate: number) { - this.channelDivs = []; - this.waveformInputs = []; - this.amplitudeSlider = []; - this.frequencySlider = []; - this.offsetSlider = []; - this.frequencySpan = []; - this.amplitudeSpan = []; - this.offsetSpan = []; - - for (let i: number = 0; i < 2; i++) { - this.channelDivs[i] = document.getElementById("channel-" + i.toString()); - this.waveformInputs[i] = []; - for (let j: number = 0; j < 3; j ++) { - this.waveformInputs[i][j] = document.getElementById("waveform-" + i.toString() + "-" + j.toString()); - } - this.amplitudeSlider[i] = document.getElementById('amplitude-slider-' + i.toString()); - this.frequencySlider[i] = document.getElementById('frequency-slider-' + i.toString()); - this.offsetSlider[i] = document.getElementById('offset-slider-' + i.toString()); - this.frequencySpan[i] = document.getElementById('frequency-' + i.toString()); - this.amplitudeSpan[i] = document.getElementById('amplitude-' + i.toString()); - this.offsetSpan[i] = document.getElementById('offset-' + i.toString()); - } + this.channelInputs = document.getElementsByClassName("modulation-channel-input"); + this.initChannelInputs(); + this.currentChannel = parseInt((document.querySelector(".modulation-channel-input:checked")).value); + this.channelElements = document.getElementsByClassName("modulation-channel"); + this.modulationInputs = document.getElementsByClassName("modulation-input"); + this.initModulationInputs(); this.update(); } update() { this.driver.getModulationStatus((status: ModulationStatus) => { - for (let channel: number = 0; channel < 2; channel++) { - for (let i = 0; i < 3; i++) { - if (status.wfmType[channel] === i) { - this.waveformInputs[channel][i].checked = true; - } else { - this.waveformInputs[channel][i].checked = false; + for (let property in status) { + if (status.hasOwnProperty(property)) { + if (["dacAmplitude", "dacFrequency", "dacOffset"].indexOf(property) > -1) { + let input = document.querySelector("input.modulation-channel[data-status='" + property + "'][data-channel='" + this.currentChannel.toString() + "']"); + let span = document.querySelector("span.modulation-channel[data-status='" + property + "'][data-channel='" + this.currentChannel.toString() + "']"); + let inputValue: string; + let spanValue: string; + + if (property === "dacAmplitude") { + inputValue = status[property][this.currentChannel].toFixed(3); + spanValue = inputValue; + } else if (property === "dacFrequency") { + inputValue = (status[property][this.currentChannel] * this.wfmSize / this.samplingRate).toFixed(3); + spanValue = (status[property][this.currentChannel] * this.wfmSize / this.samplingRate).toFixed(3); + } else if (property === "dacOffset") { + inputValue = (status[property][this.currentChannel] / 1e6).toFixed(3); + spanValue = inputValue; + } + + if (document.activeElement !== input && document.body.contains(input)) { + (input).value = inputValue; + } + + if (document.body.contains(span)) { + (span).textContent = spanValue; + } + } else if (property === "wfmType") { + let input = document.querySelector("input.modulation-channel[data-status='" + property + "'][data-channel='" + this.currentChannel.toString() + "'][value='" + status[property][this.currentChannel].toString() + "']"); + input.checked = true; } } - - const amplitude = status.dacAmplitude[channel].toFixed(3).toString(); - this.amplitudeSpan[channel].innerHTML = amplitude; - this.amplitudeSlider[channel].value = amplitude; - - const frequency = status.dacFrequency[channel] - this.frequencySpan[channel].innerHTML = (frequency / 1e6).toFixed(3).toString(); - this.frequencySlider[channel].value = (frequency * this.wfmSize / this.samplingRate).toString(); - - const offset = status.dacOffset[channel].toFixed(3).toString(); - this.offsetSpan[channel].innerHTML = offset; - this.offsetSlider[channel].value = offset; } + requestAnimationFrame( () => { this.update(); } ) }); } - switchChannel(channel: string) { - for (let i: number = 0; i < 2; i++) { - if (i === parseInt(channel)) { - this.channelDivs[i].style.display = "block"; - } else { - this.channelDivs[i].style.display = "none"; - } + initChannelInputs(): void { + for (let i = 0; i < this.channelInputs.length; i ++) { + this.channelInputs[i].addEventListener('change', (event) => { + for (let j = 0; j < this.channelElements.length; j++) { + let channel: string = (event.currentTarget).value; + (this.channelElements[j]).dataset.channel = channel; + this.currentChannel = parseInt(channel); + } + }) } } - // Waveform - - setWfmType(channel: number, wfmIndex: string): void { - this.driver.setWaveformType(channel, parseInt(wfmIndex)); - } + initModulationInputs(): void { + let events = ['change', 'input']; + for (let i = 0; i < events.length; i++) { + for (let j = 0; j < this.modulationInputs.length ; j++) { + this.modulationInputs[j].addEventListener(events[i], (event) => { + let command: string = (event.currentTarget).dataset.command; + let channel: string = (event.currentTarget).dataset.channel; + let value: string = (event.currentTarget).value; + let arg: any; + let status: string; + + if (command === "setWaveformType") { + arg = parseInt(value); + } else if (command === "setDacAmplitude") { + arg = parseFloat(value); + } else if (command === "setDacFrequency") { + arg = parseInt(value) * this.samplingRate / this.wfmSize; + } else if (command === "setDacOffset") { + arg = parseFloat(value); + } - setDacAmplitude(channel: number, amplitude: string): void { - this.driver.setDacAmplitude(channel, parseFloat(amplitude)); - } + this.driver[command](channel, arg); + }) + } + } - setDacFrequency(channel: number, frequency: string): void { - this.driver.setDacFrequency(channel, parseInt(frequency) * this.samplingRate / this.wfmSize); - } - setDacOffset(channel: number, offset: string): void { - this.driver.setDacOffset(channel, parseFloat(offset)); } } diff --git a/web/navigation.html b/web/navigation.html new file mode 100644 index 000000000..0acd5de12 --- /dev/null +++ b/web/navigation.html @@ -0,0 +1,37 @@ + \ No newline at end of file diff --git a/web/plot-basics/plot-basics.html b/web/plot-basics/plot-basics.html new file mode 100644 index 000000000..1cd184c5f --- /dev/null +++ b/web/plot-basics/plot-basics.html @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/examples/alpha250/fft/web/plot.ts b/web/plot-basics/plot-basics.ts similarity index 60% rename from examples/alpha250/fft/web/plot.ts rename to web/plot-basics/plot-basics.ts index 96931c84b..70f10105f 100644 --- a/examples/alpha250/fft/web/plot.ts +++ b/web/plot-basics/plot-basics.ts @@ -1,11 +1,9 @@ // Plot widget // (c) Koheron -class Plot { - private n_pts: number; +class PlotBasics { - private min_y: number = -200; - private max_y: number = 170; + private plotTitleSpan: HTMLSpanElement; private range_x: jquery.flot.range; private range_y: jquery.flot.range; @@ -13,14 +11,8 @@ class Plot { private reset_range: boolean; private options: jquery.flot.plotOptions; private plot: jquery.flot.plot; - - private yUnit: string; - private yLabel: string; - private plot_data: Array>; - private isMeasure: boolean = true; - private isPeakDetection: boolean = true; private peakDatapointSpan: HTMLSpanElement; private peakDatapoint: number[]; @@ -31,24 +23,27 @@ class Plot { private clickDatapointSpan: HTMLSpanElement; private clickDatapoint: number[]; - private exportDataFilename: HTMLLinkElement; - private exportPlotFilename: HTMLLinkElement; + constructor(document: Document, private plot_placeholder: JQuery, private plotDriver: Plot, private n_pts: number, private x_min, private x_max, private y_min, private y_max, + private driver, private rangeFunction, private plotTitle: string) { - constructor(document: Document, private plot_placeholder: JQuery, private fft: FFT, private control: Control) { - this.setPlot(); + this.plotTitleSpan = document.getElementById("plot-title"); + this.plotTitleSpan.textContent = this.plotTitle; this.range_x = {}; - this.range_x.from = 0; - this.range_x.to = this.fft.status.fs / 1E6 / 2; - + this.range_x.from = this.x_min; + this.range_x.to = this.x_max; this.range_y = {}; - this.range_y.from = this.min_y; - this.range_y.to = this.max_y; - - this.n_pts = this.fft.fft_size / 2; + this.range_y.from = this.y_min; + this.range_y.to = this.y_max; - this.yUnit = "dBm-Hz"; - this.yLabel = "Power Spectral Density"; + this.setPlot(this.range_x.from, this.range_x.to, this.range_y.from, this.range_y.to); + this.rangeSelect(this.rangeFunction); + this.dblClick(this.x_max, this.rangeFunction); + this.onWheel(this.x_max, this.rangeFunction); + this.showHoverPoint(); + this.showClickPoint(); + this.plotLeave(); + this.reset_range = true; this.hoverDatapointSpan = document.getElementById("hover-datapoint"); this.hoverDatapoint = []; @@ -57,42 +52,28 @@ class Plot { this.clickDatapoint = []; this.peakDatapointSpan = document.getElementById("peak-datapoint"); - this.peakDatapoint = []; - - this.exportDataFilename = document.getElementById("export-data-filename"); - this.exportPlotFilename = document.getElementById("export-plot-filename"); this.plot_data = []; - this.updatePlot(); + this.initUnitInputs(); + this.initPeakDetection(); } - updatePlot() { - this.fft.read_psd( (psd: Float32Array) => { - this.redraw(psd, () => { - requestAnimationFrame( () => { this.updatePlot(); } ); - }); - }); - } - - setPlot() { + setPlot(x_min: number, x_max: number, y_min: number, y_max: number) { this.reset_range = false; - let labelAttribute: string = ""; - labelAttribute += " style='font-size: 16px; color: #333'"; - this.options = { canvas: true, series: { shadowSize: 0 // Drawing is faster without shadows }, yaxis: { - min: this.min_y, - max: this.max_y + min: y_min, + max: y_max }, xaxis: { - min: 0, - max: this.fft.status.fs / 1E6 / 2, + min: x_min, + max: x_max, show: true }, grid: { @@ -102,8 +83,8 @@ class Plot { }, borderColor: "#d5d5d5", borderWidth: 1, - clickable: this.isMeasure, - hoverable: this.isMeasure, + clickable: true, + hoverable: true, autoHighlight: true }, selection: { @@ -114,23 +95,16 @@ class Plot { show: true, noColumns: 0, labelFormatter: (label: string, series: any): string => { - return "" + label + "\t" + return "" + label + "\t" }, margin: 0, position: "ne", } } - this.rangeSelect(); - this.dblClick(); - this.onWheel(); - this.showHoverPoint(); - this.showClickPoint(); - this.plotLeave(); - this.reset_range = true; } - rangeSelect() { + rangeSelect(rangeFunction: string) { this.plot_placeholder.bind("plotselected", (event: JQueryEventObject, ranges: jquery.flot.ranges) => { // Clamp the zooming to prevent external zoom @@ -148,40 +122,27 @@ class Plot { this.range_y.from = ranges.yaxis.from; this.range_y.to = ranges.yaxis.to; - this.resetRange(); + if (rangeFunction.length > 0) { + this.driver[rangeFunction](ranges.xaxis); + } + + this.reset_range = true; }); } // A double click on the plot resets to full span - dblClick() { + dblClick(max_x: number, rangeFunction: string) { this.plot_placeholder.bind("dblclick", (evt: JQueryEventObject) => { this.range_x.from = 0; - this.range_x.to = this.fft.status.fs / 1E6 / 2; - + this.range_x.to = max_x; this.range_y = {}; - this.resetRange(); + if (rangeFunction.length > 0) { + this.driver[rangeFunction](this.range_x); + } + this.reset_range = true; }); } - resetRange() { - this.reset_range = true; - } - - convertValue(inValue: number, outUnit: string): number { - // inValue in W / Hz - let outValue: number = 0; - - if (outUnit === "dBm-Hz") { - outValue = 10 * Math.log(inValue / 1E-3) / Math.LN10; - } else if (outUnit === "dBm") { - outValue = 10 * Math.log(inValue * (this.fft.status.W2 / this.fft.status.W1) * this.fft.status.fs / this.fft.fft_size / 1E-3) / Math.LN10; - } else if (outUnit === "nv-rtHz") { - outValue = Math.sqrt(50 * inValue) * 1E9; - } - - return outValue; - } - updateDatapointSpan(datapoint: number[], datapointSpan: HTMLSpanElement): void { let positionX: number = (this.plot.pointOffset({x: datapoint[0], y: datapoint[1] })).left; let positionY: number = (this.plot.pointOffset({x: datapoint[0], y: datapoint[1] })).top; @@ -201,21 +162,16 @@ class Plot { } } - redraw(psd: Float32Array, callback: () => void) { - this.peakDatapoint = [ this.fft.status.fs / 1E6 / 2 / this.n_pts , this.convertValue(psd[0], this.yUnit)]; + redraw(plot_data: number[][], n_pts: number, peakDatapoint: number[], ylabel: string, callback: () => void) { - for (let i: number = 0; i <= this.n_pts; i++) { - let freq: number = (i + 1) * this.fft.status.fs / 1E6 / 2 / this.n_pts; // MHz - let convertedPsd: number = this.convertValue(psd[i], this.yUnit); - this.plot_data[i] = [freq, convertedPsd]; - - if (this.peakDatapoint[1] < this.plot_data[i][1]) { - this.peakDatapoint[0] = this.plot_data[i][0]; - this.peakDatapoint[1] = this.plot_data[i][1]; + for (let i: number = 0; i <= n_pts; i++) { + if (peakDatapoint[1] < plot_data[i][1]) { + peakDatapoint[0] = plot_data[i][0]; + peakDatapoint[1] = plot_data[i][1]; } } - const plt_data: jquery.flot.dataSeries[] = [{label: this.yLabel, data: this.plot_data}]; + const plt_data: jquery.flot.dataSeries[] = [{label: ylabel, data: plot_data}]; if (this.reset_range) { this.options.xaxis.min = this.range_x.from; @@ -240,7 +196,7 @@ class Plot { if (this.clickDatapoint.length > 0) { let i: number; - for (i = 0; i < this.n_pts; i++) { + for (i = 0; i < n_pts; i++) { if (localData[0]['data'][i][0] > this.clickDatapoint[0]) { break; } @@ -258,7 +214,7 @@ class Plot { } if (this.range_x.from < this.clickDatapoint[0] && this.clickDatapoint[0] < this.range_x.to && - this.range_y.from < this.clickDatapoint[1] && this.clickDatapoint[1] < this.range_y.to) { + this.range_y.from < this.clickDatapoint[1] && this.clickDatapoint[1] < this.range_y.to) { this.updateDatapointSpan(this.clickDatapoint, this.clickDatapointSpan); this.clickDatapointSpan.style.display = "inline-block"; this.plot.highlight(localData[0], this.clickDatapoint); @@ -268,26 +224,99 @@ class Plot { } if (this.isPeakDetection) { - this.plot.unhighlight(localData[0], this.peakDatapoint); + this.plot.unhighlight(localData[0], peakDatapoint); - if (this.range_x.from < this.peakDatapoint[0] && this.peakDatapoint[0] < this.range_x.to && - this.range_y.from < this.peakDatapoint[1] && this.peakDatapoint[1] < this.range_y.to) { - this.updateDatapointSpan(this.peakDatapoint, this.peakDatapointSpan); - this.plot.highlight(localData[0], this.peakDatapoint); + if (this.range_x.from < peakDatapoint[0] && peakDatapoint[0] < this.range_x.to && + this.range_y.from < peakDatapoint[1] && peakDatapoint[1] < this.range_y.to) { + this.updateDatapointSpan(peakDatapoint, this.peakDatapointSpan); + this.plot.highlight(localData[0], peakDatapoint); this.peakDatapointSpan.style.display = "inline-block"; } else { - this.plot.unhighlight(localData[0], this.peakDatapoint); + this.plot.unhighlight(localData[0], peakDatapoint); this.peakDatapointSpan.style.display = "none"; } } else { - this.plot.unhighlight(localData[0], this.peakDatapoint); + this.plot.unhighlight(localData[0], peakDatapoint); this.peakDatapointSpan.style.display = "none"; } callback(); } - onWheel(): void { + + redrawRange(data: number[][], range_x: jquery.flot.range, ylabel: string, callback: () => void): void { + + const plt_data: jquery.flot.dataSeries[] = [{label: ylabel, data: data}]; + + if (data.length == 0) { + callback(); + return; + } + + if (this.reset_range) { + this.options.xaxis.min = range_x.from; + this.options.xaxis.max = range_x.to; + this.options.yaxis.min = this.range_y.from; + this.options.yaxis.max = this.range_y.to; + this.plot = $.plot(this.plot_placeholder, plt_data, this.options); + this.plot.setupGrid(); + this.reset_range = false; + } else { + this.plot.setData(plt_data); + this.plot.draw(); + } + + callback(); + } + + + redrawTwoChannels(ch0: number[][], ch1: number[][], + range_x: jquery.flot.range, label1: string, label2: string, is_channel_1: boolean, is_channel_2: boolean, callback: () => void): void { + + if (ch0.length === 0 || ch1.length === 0) { + callback(); + return; + } + + let plotCh0: number[][] = []; + let plotCh1: number[][] = []; + + if (is_channel_1 && is_channel_2) { + plotCh0 = ch0; + plotCh1 = ch1; + // plotData = [ch0, ch1]; + } else if (is_channel_1 && !is_channel_2) { + plotCh0 = ch0; + plotCh1 = []; + } else if (!is_channel_1 && is_channel_2) { + plotCh0 = []; + plotCh1 = ch1; + } else { + plotCh0 = []; + plotCh1 = []; + } + + const plt_data: jquery.flot.dataSeries[] = [{label: label1, data: plotCh0}, {label: label2, data: plotCh1}]; + + if (this.reset_range) { + this.options.xaxis.min = range_x.from; + this.options.xaxis.max = range_x.to; + this.options.yaxis.min = this.range_y.from; + this.options.yaxis.max = this.range_y.to; + + this.plot = $.plot(this.plot_placeholder, plt_data, this.options); + + this.plot.setupGrid(); + this.reset_range = false; + } else { + this.plot.setData(plt_data); + this.plot.draw(); + } + + callback(); + } + + onWheel(max_x: number, rangeFunction: string): void { this.plot_placeholder.bind("wheel", (evt: JQueryEventObject) => { let delta: number = (evt.originalEvent).deltaX + (evt.originalEvent).deltaY; @@ -304,22 +333,26 @@ class Plot { to: y0 - (1 + zoomRatio * delta) * (y0 - this.plot.getAxes().yaxis.max) }; - this.resetRange(); + this.reset_range = true; return false; } else if ((evt.originalEvent).altKey) { // Zoom X const positionX: number = (evt.originalEvent).pageX - this.plot.offset().left; const x0: any = this.plot.getAxes().xaxis.c2p(positionX); - if (x0 < 0 || x0 > this.fft.status.fs / 1E6 / 2) { + if (x0 < 0 || x0 > max_x) { return; } this.range_x = { from: Math.max(x0 - (1 + zoomRatio * delta) * (x0 - this.plot.getAxes().xaxis.min), 0), - to: Math.min(x0 - (1 + zoomRatio * delta) * (x0 - this.plot.getAxes().xaxis.max), this.fft.status.fs / 1E6 / 2) + to: Math.min(x0 - (1 + zoomRatio * delta) * (x0 - this.plot.getAxes().xaxis.max), max_x) }; - this.resetRange(); + if (rangeFunction.length > 0) { + this.driver[rangeFunction](this.range_x); + } + + this.reset_range = true; return false; } @@ -327,16 +360,25 @@ class Plot { }); } - changeYUnit(yUnit: string): void { - this.yUnit = yUnit; - this.resetRange(); + initUnitInputs(): void { + let unitInputs: HTMLInputElement[] = document.getElementsByClassName("unit-input"); + for (let i = 0; i < unitInputs.length; i ++) { + unitInputs[i].addEventListener( 'change', (event) => { + this.reset_range = true; + }) + } } - detectPeak(): void { - if (this.isPeakDetection) { - this.isPeakDetection = false; - } else { - this.isPeakDetection = true; + initPeakDetection(): void { + let peakInputs: HTMLInputElement[] = document.getElementsByClassName("peak-input"); + for (let i = 0; i < peakInputs.length; i ++) { + peakInputs[i].addEventListener( 'change', (event) => { + if (this.isPeakDetection) { + this.isPeakDetection = false; + } else { + this.isPeakDetection = true; + } + }) } } @@ -375,41 +417,4 @@ class Plot { }); } - exportData() { - let csvContent = "data:text/csv;charset=utf-8,"; - - csvContent += "Koheron Alpha \n"; - - let dateTime = new Date(); - csvContent += dateTime.getDate() + "/" + (dateTime.getMonth()+1) + "/" + dateTime.getFullYear() + " " ; - csvContent += dateTime.getHours() + ":" + dateTime.getMinutes() + ":" + dateTime.getSeconds() + "\n"; - - csvContent += "\n"; - csvContent += '"Window",' + (this.control.fftWindowIndex).toString() + "\n"; - csvContent += '"Input channel",' + (this.fft.status.channel).toString() + "\n"; - csvContent += '"Sampling frequency (MHz)",' + (this.fft.status.fs / 1e6).toString() + "\n"; - csvContent += '"Reference clock (10 MHz)",' + this.control.referenceClock + "\n"; - csvContent += '"Channel 0 DDS frequency (MHz)",' + (this.fft.status.dds_freq[0] / 1e6).toString() + "\n"; - csvContent += '"Channel 1 DDS frequency (MHz)",' + (this.fft.status.dds_freq[1] / 1e6).toString() + "\n"; - - csvContent += "\n\n"; - - csvContent += '"Frequency (MHz)","Power spectral density (' + this.yUnit.replace("-", "/") + ')" \n'; - - this.plot_data.forEach( (rowArray) => { - let row = rowArray.join(","); - csvContent += row + "\n"; - }); - - this.exportDataFilename.href = encodeURI(csvContent); - this.exportDataFilename.click(); - } - - exportPlot(): void { - let canvas = this.plot.getCanvas(); - let imagePng = canvas.toDataURL("image/png").replace("image/png", "image/octet-stream"); - - this.exportPlotFilename.href = imagePng; - this.exportPlotFilename.click(); - } }