Add new measurement recording infrastructure

This commit is contained in:
jaseg 2025-06-23 16:40:07 +02:00
parent b8812245ec
commit c75c48e53c
5 changed files with 259 additions and 198 deletions

View file

@ -21,16 +21,12 @@ svd_load upstream/stm32square/svd/STM32G474.svd
break debug_plot_hook
commands
dump binary memory /tmp/adc1_readings.bin adc1_readings adc1_readings+(sizeof(adc1_readings)/sizeof(*adc1_readings))
dump binary memory /tmp/adc1_variance.bin adc1_variance adc1_variance+(sizeof(adc1_readings)/sizeof(*adc1_readings))
dump binary memory /tmp/adc2_readings.bin adc2_readings adc2_readings+(sizeof(adc1_readings)/sizeof(*adc1_readings))
dump binary memory /tmp/adc2_variance.bin adc2_variance adc2_variance+(sizeof(adc1_readings)/sizeof(*adc1_readings))
dump binary memory /tmp/adc_stats.bin &host_data &host_data+1
restore /tmp/dbg_settings.bin binary dbg_settings
p/x dbg_settings
cont
end
dump binary memory /tmp/dbg_settings.bin &dbg_settings &dbg_settings+1
load
dump binary memory /tmp/dbg_settings.bin &dbg_settings &dbg_settings+1
cont

View file

@ -22,15 +22,14 @@ dbg_settings = None
def index():
return render_template('index.html')
def watch_adc_graphs(sock):
def watch_adc_graphs(sock, stat_path='/tmp/adc_stats.bin'):
stat_path = Path(stat_path)
def handler():
Path('/tmp/adc1_readings.bin').touch()
Path('/tmp/adc2_readings.bin').touch()
Path('/tmp/adc1_variance.bin').touch()
Path('/tmp/adc2_variance.bin').touch()
stat_path.touch()
in_handle = inotify.adapters.Inotify()
in_handle.add_watch('/tmp/adc2_variance.bin', mask=inotify.constants.IN_CLOSE_WRITE)
in_handle.add_watch(str(stat_path), mask=inotify.constants.IN_CLOSE_WRITE)
for event in in_handle.event_gen(yield_nones=False):
if dbg_settings is None: # not yet initialized from UI
print('ignoring update')
@ -40,16 +39,43 @@ def watch_adc_graphs(sock):
oversampling = int(dbg_settings['oversampling']);
scale = 3.3 / (2**12) / oversampling;
def read_data(fn, scale):
data = np.frombuffer(Path(fn).read_bytes(), dtype=np.uint32)
data = data[::-1] # restore causality ;)
data = data * scale
return data.tolist()
sock.emit('graph_update', {'adc1': read_data('/tmp/adc1_readings.bin', scale),
'var1': read_data('/tmp/adc1_variance.bin', scale**2/oversampling),
'adc2': read_data('/tmp/adc2_readings.bin', scale),
'var2': read_data('/tmp/adc2_variance.bin', scale**2/oversampling)})
data = np.frombuffer(stat_path.read_bytes(), dtype=np.uint32)
# cast to python ints for arbitrary precision arithmetic
mcu_serial = int(data[0]) | (int(data[1])<<32) | (int(data[2])<<64);
mcu_serial = f'{mcu_serial:024x}'
chip_type = {
'203632423631500f003f0034': 'pi3hdx12211',
'b': 'tdp0604',
}.get(mcu_serial, 'unknown')
data = data[3:].astype(float).reshape((7, 4, -1))
data[:,:,:] = data[:,:,::-1] # restore causality ;)
# Scale mean data
data[:,0,:] *= scale
data[:,2,:] *= scale
# Scale variance data
data[:,1,:] *= scale**2/oversampling
data[:,3,:] *= scale**2/oversampling
sock.emit('graph_update', {
'mcu_serial': mcu_serial,
'chip_type': chip_type,
'data': {key: {
'adc1': data[idx][0].tolist(),
'var1': data[idx][1].tolist(),
'adc2': data[idx][2].tolist(),
'var2': data[idx][3].tolist(),
} for idx, key in enumerate([
'cal',
'open_fwd',
'open_flip',
'load_fwd',
'load_flip',
'short_fwd',
'short_flip'])}})
adc_thr = threading.Thread(target=handler, daemon=True)
adc_thr.start()
@ -62,11 +88,7 @@ def handle(data):
else:
dbg_settings.update(data)
out = 0
for i, key in enumerate(['mesh_flip_n', 'mesh_flip', 'rf_term', 'rf_term_short']):
if dbg_settings[key]:
out |= 1<<i
out |= (int(dbg_settings['sampling_phase_var']) & 0xff) << 4
out = (int(dbg_settings['sampling_phase_var']) & 0xff) << 4
out |= (int(dbg_settings['sampling_width']) & 0xff) << 12
out |= (int(dbg_settings['stimulus_offset']) & 0xff) << 20
out1 = (int(dbg_settings['oversampling']) & 0x1ff)

View file

@ -11,10 +11,30 @@ int sampling_phase = 0;
int adc_backoff = 60;
uint16_t adc1_raw[512];
uint16_t adc2_raw[COUNT_OF(adc1_raw)];
uint32_t adc1_readings[1024];
uint32_t adc2_readings[COUNT_OF(adc1_readings)];
uint32_t adc1_variance[COUNT_OF(adc1_readings)];
uint32_t adc2_variance[COUNT_OF(adc1_readings)];
#define ADC_DELAY_WINDOW_SIZE 768
struct adc_stat_buffer {
uint32_t adc1_mean[ADC_DELAY_WINDOW_SIZE];
uint32_t adc1_var[ADC_DELAY_WINDOW_SIZE];
uint32_t adc2_mean[ADC_DELAY_WINDOW_SIZE];
uint32_t adc2_var[ADC_DELAY_WINDOW_SIZE];
};
enum {
MEAS_PB_CAL = 0,
MEAS_PB_OPEN_FWD = 1,
MEAS_PB_OPEN_FLIP = 2,
MEAS_PB_LOAD_FWD = 3,
MEAS_PB_LOAD_FLIP = 4,
MEAS_PB_SHORT_FWD = 5,
MEAS_PB_SHORT_FLIP = 6,
_MEAS_PB_COUNT = 7
} meas_pb_now = 0;
struct {
uint32_t mcu_serial[3];
struct adc_stat_buffer adc_stats[_MEAS_PB_COUNT];
} host_data;
/* Bootup values. Will be overwritten with defaults from web interface HTML on launch. */
const int default_sampling_phase_var = 2;
@ -36,32 +56,56 @@ enum dbg_settings_mask {
volatile uint32_t dbg_settings[2] = {
(default_sampling_phase_var<<4) |
(default_sampling_width<<12) |
(default_stim_offset<<20) |
DBG_MESH_FLIP_N,
(default_stim_offset<<20),
(default_oversampling<<0)};
void debug_plot_hook(void);
void debug_plot_hook(void){
if (dbg_settings[0] & DBG_MESH_FLIP) {
void meas_pb_config();
void meas_pb_config() {
struct flags {
bool flip,
flip_n,
term,
term_short;
};
struct flags config[_MEAS_PB_COUNT] = {
[MEAS_PB_CAL] = {.flip=0, .flip_n=0, .term=0, .term_short=0},
[MEAS_PB_OPEN_FWD] = {.flip=0, .flip_n=1, .term=0, .term_short=0},
[MEAS_PB_OPEN_FLIP] = {.flip=1, .flip_n=0, .term=0, .term_short=0},
[MEAS_PB_LOAD_FWD] = {.flip=0, .flip_n=1, .term=1, .term_short=0},
[MEAS_PB_LOAD_FLIP] = {.flip=1, .flip_n=0, .term=1, .term_short=0},
[MEAS_PB_SHORT_FWD] = {.flip=0, .flip_n=1, .term=1, .term_short=1},
[MEAS_PB_SHORT_FLIP] = {.flip=1, .flip_n=0, .term=1, .term_short=1},
};
struct flags now = config[meas_pb_now];
if (now.flip) {
GPIOC->BSRR = 1<<4;
} else {
GPIOC->BRR = 1<<4;
}
if (dbg_settings[0] & DBG_MESH_FLIP_N) {
if (now.flip_n) {
GPIOB->BSRR = 1<<1;
} else {
GPIOB->BRR = 1<<1;
}
if (dbg_settings[0] & DBG_RF_TERM) {
if (now.term) {
GPIOC->BSRR = 1<<14;
} else {
GPIOC->BRR = 1<<14;
}
if (dbg_settings[0] & DBG_RF_TERM_SHORT) {
if (now.term_short) {
GPIOC->BSRR = 1<<15;
} else {
GPIOC->BRR = 1<<15;
}
}
void debug_plot_hook(void);
void debug_plot_hook(void){
sampling_phase_var = (dbg_settings[0]>>4) & 0xff;
sampling_width = (dbg_settings[0]>>12) & 0xff;
stim_offset = (dbg_settings[0]>>20) & 0xff;
@ -232,6 +276,11 @@ int main(void) {
GPIOC->BRR = (1<<4) | (1<<7) | (1<<14) | (1<<15); /* Clear all outputs on this bank */
GPIOC->BSRR = 0xf;
const uint32_t *mcu_uid_reg = (const uint32_t *)UID_BASE;
host_data.mcu_serial[0] = mcu_uid_reg[0];
host_data.mcu_serial[1] = mcu_uid_reg[1];
host_data.mcu_serial[2] = mcu_uid_reg[2];
/* GPIOD:
* 2 - SFP5
*
@ -335,7 +384,7 @@ int main(void) {
HRTIM1->sTimerxRegs[1].TIMxCR = HRTIM_TIMCR_MSTU;
HRTIM1->sTimerxRegs[1].PERxR = HRTIM1->sMasterRegs.MPER;
HRTIM1->sTimerxRegs[1].RSTxR = HRTIM_RSTR_MSTPER;
int sampling_phase_var = (dbg_settings[0]>>4) & 0x3f;
//int sampling_phase_var = (dbg_settings[0]>>4) & 0x3f;
//HRTIM1->sTimerxRegs[1].CMP3xR = (global_sampling_offset + sampling_phase_var) * 0x20;
//HRTIM1->sTimerxRegs[1].CMP4xR = (global_sampling_offset + sampling_phase_var) * 0x20;
@ -479,7 +528,7 @@ void DMA1_Channel1_IRQHandler(void) {
* width anyway. */
int mean1 = 0, sqmean1 = 0;
int mean2 = 0, sqmean2 = 0;
for (size_t i=2; i<oversampling+2; i++) {
for (ssize_t i=2; i<oversampling+2; i++) {
int entry = adc1_raw[i];
mean1 += entry;
sqmean1 += entry*entry;
@ -488,22 +537,27 @@ void DMA1_Channel1_IRQHandler(void) {
mean2 += entry;
sqmean2 += entry*entry;
}
adc1_readings[sampling_phase] = mean1;
adc1_variance[sampling_phase] = sqmean1*oversampling - mean1*mean1;
adc2_readings[sampling_phase] = mean2;
adc2_variance[sampling_phase] = sqmean2*oversampling - mean2*mean2;
host_data.adc_stats[meas_pb_now].adc1_mean[sampling_phase] = mean1;
host_data.adc_stats[meas_pb_now].adc1_var[sampling_phase] = sqmean1*oversampling - mean1*mean1;
host_data.adc_stats[meas_pb_now].adc2_mean[sampling_phase] = mean2;
host_data.adc_stats[meas_pb_now].adc2_var[sampling_phase] = sqmean2*oversampling - mean2*mean2;
sampling_phase ++;
if (sampling_phase == COUNT_OF(adc1_readings)) {
if (sampling_phase == ADC_DELAY_WINDOW_SIZE) {
sampling_phase = 0;
GPIOC->BRR = 1<<2;
debug_plot_hook();
GPIOC->BSRR = 1<<2;
for (size_t i=0; i<COUNT_OF(adc1_readings); i++) {
adc1_readings[i] = 0;
}
for (size_t i=0; i<COUNT_OF(adc2_readings); i++) {
adc2_readings[i] = 0;
if (meas_pb_now < _MEAS_PB_COUNT) {
meas_pb_now ++;
meas_pb_config();
} else {
GPIOC->BRR = 1<<2;
debug_plot_hook();
GPIOC->BSRR = 1<<2;
meas_pb_now = 0;
memset(&host_data.adc_stats, 0, sizeof(host_data.adc_stats));
}
}

View file

@ -9,31 +9,17 @@
<script src="{{url_for('static', filename='plotly-2.35.2.min.js')}}" charset="utf-8"></script>
<script src="{{url_for('static', filename='socket.io.min.js')}}" charset="utf-8"></script>
<style>
#plot_adc1, #plot_adc2 {
width: 600px;
height: 250px;
#plot-grid {
display: grid;
width: 100%;
grid-template-columns: repeat(3, 1fr);
/* grid-template-rows: repeat(6, 600px); */
}
</style>
</head>
<body>
<h4>Device settings</h4>
<div>
<label for="cb_dev_mesh_flip">
<input type="checkbox" id="cb_dev_mesh_flip" data-dev-setting="mesh_flip"/>
MESH_FLIP
</label>
<label for="cb_dev_mesh_flip_n">
<input type="checkbox" id="cb_dev_mesh_flip_n" checked data-dev-setting="mesh_flip_n"/>
MESH_FLIP_N
</label>
<label for="cb_dev_rf_term">
<input type="checkbox" id="cb_dev_rf_term" data-dev-setting="rf_term"/>
RF_TERM
</label>
<label for="cb_dev_rf_term_short">
<input type="checkbox" id="cb_dev_rf_term_short" data-dev-setting="rf_term_short"/>
RF_TERM_SHORT
</label>
<label for="sampling_phase_inp">
Sampling phase <input type="number" id="sampling_phase_inp" min="0" max="180" step="1" value="6" data-dev-setting="sampling_phase_var"/>
</label>
@ -50,122 +36,84 @@
<h4>Download data</h4>
<div class="download">
<div>
<label for="dl_meta_info">Meta info: <input type="text" id="dl_meta_info"/> <a id="download-button" href="#">Download</a>
</div>
<div>
<input type="radio" id="dl_meta_suffix_0" name="meta_suffix" value=""/>
<label for="dl_meta_suffix_1">&lt;none&gt;</label>
<input type="radio" id="dl_meta_suffix_1" name="meta_suffix" value="_baseline1"/>
<label for="dl_meta_suffix_1">baseline1</label>
<input type="radio" id="dl_meta_suffix_2" name="meta_suffix" value="_short1"/>
<label for="dl_meta_suffix_2">short1</label>
<input type="radio" id="dl_meta_suffix_3" name="meta_suffix" value="_short2"/>
<label for="dl_meta_suffix_3">short2</label>
<input type="radio" id="dl_meta_suffix_4" name="meta_suffix" value="_short3"/>
<label for="dl_meta_suffix_4">short3</label>
<input type="radio" id="dl_meta_suffix_5" name="meta_suffix" value="_baseline2"/>
<label for="dl_meta_suffix_5">baseline2</label>
<input type="radio" id="dl_meta_suffix_6" name="meta_suffix" value="_probe1a"/>
<label for="dl_meta_suffix_6">probe1a</label>
<input type="radio" id="dl_meta_suffix_7" name="meta_suffix" value="_probe1b"/>
<label for="dl_meta_suffix_7">probe1b</label>
<input type="radio" id="dl_meta_suffix_8" name="meta_suffix" value="_probe2a"/>
<label for="dl_meta_suffix_8">probe2a</label>
<input type="radio" id="dl_meta_suffix_9" name="meta_suffix" value="_probe2b"/>
<label for="dl_meta_suffix_9">probe2b</label>
<input type="radio" id="dl_meta_suffix_10" name="meta_suffix" value="_probe3a"/>
<label for="dl_meta_suffix_10">probe3a</label>
<input type="radio" id="dl_meta_suffix_11" name="meta_suffix" value="_probe3b"/>
<label for="dl_meta_suffix_11">probe3b</label>
<input type="radio" id="dl_meta_suffix_12" name="meta_suffix" value="_baseline3"/>
<label for="dl_meta_suffix_12">baseline3</label>
<input type="radio" id="dl_meta_suffix_13" name="meta_suffix" value="_cut"/>
<label for="dl_meta_suffix_13">cut</label>
<input type="radio" id="dl_meta_suffix_14" name="meta_suffix" value="_baseline4"/>
<label for="dl_meta_suffix_14">baseline4</label>
</div>
<form id="download_form">
<div>
<label for="dl_meta_info">Meta info: <input type="text" id="dl_meta_info"/></label>
<label for="dl_meta_specimen">Specimen: <input type="text" id="dl_meta_specimen"/></label>
<label for="dl_meta_mcu">MCU Serial: <input type="text" id="dl_meta_mcu" readonly/></label>
<label for="dl_meta_chip">Driver IC: <input type="text" id="dl_meta_chip" readonly/></label>
<input type="submit" value="Download"/>
</div>
</form>
</div>
<h4 id="adc1_heading">ADC 1</h4>
<div id="plot_adc1"></div>
<div class="measurement" id="meas_adc1_thr">
<h5>Threshold</h5>
<div id="plot-grid">
<adc-plot adc-index="1" playbook-item="open_fwd"></adc-plot>
<adc-plot adc-index="1" playbook-item="load_fwd"></adc-plot>
<adc-plot adc-index="1" playbook-item="short_fwd"></adc-plot>
<label for="adc1_thr_v_spin">
Voltage <input type="number" id="adc1_thr_v_spin" class="v_spin" min="-0.3" max="3.6" step="0.01"/> V
</label>
<adc-plot adc-index="2" playbook-item="open_fwd"></adc-plot>
<adc-plot adc-index="2" playbook-item="load_fwd"></adc-plot>
<adc-plot adc-index="2" playbook-item="short_fwd"></adc-plot>
<label for="adc1_thr_v_spin">
Anchor time: <input type="number" id="adc1_thr_v_spin" class="t_spin" min="0" max="500" step="1"/> ns
</label>
<adc-plot adc-index="1" playbook-item="open_flip"></adc-plot>
<adc-plot adc-index="1" playbook-item="load_flip"></adc-plot>
<adc-plot adc-index="1" playbook-item="short_flip"></adc-plot>
<div class="stats" id="table_adc1">
<table>
<tr><td></td><th>10</th><th>100</th><th>1000</th></tr>
<tr><th>Mean [ns]</th>
<td class="mean_10"></td>
<td class="mean_100"></td>
<td class="mean_1000"></td>
</tr>
<tr><th>Stdev [ps]</th>
<td class="stdev_10"></td>
<td class="stdev_100"></td>
<td class="stdev_1000"></td>
</tr>
<tr><th>Min [ns]</th>
<td class="min_10"></td>
<td class="min_100"></td>
<td class="min_1000"></td>
</tr>
<tr><th>Max [ns]</th>
<td class="max_10"></td>
<td class="max_100"></td>
<td class="max_1000"></td>
</tr>
</table>
</div>
<adc-plot adc-index="2" playbook-item="open_flip"></adc-plot>
<adc-plot adc-index="2" playbook-item="load_flip"></adc-plot>
<adc-plot adc-index="2" playbook-item="short_flip"></adc-plot>
<div></div>
<adc-plot adc-index="1" playbook-item="cal"></adc-plot>
<div></div>
<div></div>
<adc-plot adc-index="2" playbook-item="cal"></adc-plot>
<div></div>
</div>
<h4 id="adc2_heading">ADC 2</h4>
<div id="plot_adc2"></div>
<div class="measurement" id="meas_adc2_thr">
<h5>Threshold</h5>
<template id="adc_plot_template">
<h4 id="heading"></h4>
<div id="plot"></div>
<div class="measurement" id="measurement">
<h5>Threshold</h5>
<label for="adc2_thr_v_spin">
Voltage <input type="number" id="adc2_thr_v_spin" class="v_spin" min="-0.3" max="3.6" step="0.01"/> V
</label>
<label for="thr_v_spin">
Voltage <input type="number" id="thr_v_spin" class="v_spin" min="-0.3" max="3.6" step="0.01"/> V
</label>
<label for="adc2_thr_v_spin">
Anchor time: <input type="number" id="adc2_thr_v_spin" class="t_spin" min="0" max="500" step="1"/> ns
</label>
<label for="thr_t_spin">
Anchor time: <input type="number" id="thr_t_spin" class="t_spin" min="0" max="500" step="1"/> ns
</label>
<div class="stats" id="table_adc2">
<table>
<tr><td></td><th>10</th><th>100</th><th>1000</th></tr>
<tr><th>Mean [ns]</th>
<td class="mean_10"></td>
<td class="mean_100"></td>
<td class="mean_1000"></td>
</tr>
<tr><th>Stdev [ps]</th>
<td class="stdev_10"></td>
<td class="stdev_100"></td>
<td class="stdev_1000"></td>
</tr>
<tr><th>Min [ns]</th>
<td class="min_10"></td>
<td class="min_100"></td>
<td class="min_1000"></td>
</tr>
<tr><th>Max [ns]</th>
<td class="max_10"></td>
<td class="max_100"></td>
<td class="max_1000"></td>
</tr>
</table>
<div class="stats" id="table">
<table>
<tr><td></td><th>10</th><th>100</th><th>1000</th></tr>
<tr><th>Mean [ns]</th>
<td class="mean_10"></td>
<td class="mean_100"></td>
<td class="mean_1000"></td>
</tr>
<tr><th>Stdev [ps]</th>
<td class="stdev_10"></td>
<td class="stdev_100"></td>
<td class="stdev_1000"></td>
</tr>
<tr><th>Min [ns]</th>
<td class="min_10"></td>
<td class="min_100"></td>
<td class="min_1000"></td>
</tr>
<tr><th>Max [ns]</th>
<td class="max_10"></td>
<td class="max_100"></td>
<td class="max_1000"></td>
</tr>
</table>
</div>
</div>
</div>
</template>
<script>
class ThresholdStats {
@ -243,8 +191,8 @@
class DataPlot {
constructor(plot_div, table_div, meas_div, heading_div) {
this.lock = false;
this.heading = document.querySelector(heading_div);
this.div = document.querySelector(plot_div);
this.heading = heading_div;
this.div = plot_div;
this.layout_settings = {
margin: {t: 20, l: 60, r: 20, b: 40},
hovermode: 'closest',
@ -256,7 +204,7 @@
{'x': [], 'y': [], 'type': 'scatter'},
{'x': [], 'y': [], 'type': 'scatter'}
], this.layout_settings);
this.stats = new ThresholdStats(document.querySelector(table_div), document.querySelector(meas_div));
this.stats = new ThresholdStats(table_div, meas_div);
this.div.on('plotly_click', (evt) => {
this.stats.set_thr(evt.points[0].y, evt.points[0].x);
@ -314,36 +262,77 @@
}
};
adc1_plot = new DataPlot('#plot_adc1', '#table_adc1', '#meas_adc1_thr', '#adc1_heading');
adc2_plot = new DataPlot('#plot_adc2', '#table_adc2', '#meas_adc2_thr', '#adc2_heading');
adc_update_listeners = {};
customElements.define("adc-plot",
class extends HTMLElement {
constructor () {
super();
let template = document.getElementById("adc_plot_template");
const shadowRoot = this.attachShadow({"mode": "open"});
shadowRoot.appendChild(template.content.cloneNode(true));
let heading = shadowRoot.getElementById("heading");
let adc_index = this.getAttribute("adc-index");
let playbook_item = this.getAttribute("playbook-item");
heading.innerText = `ADC ${adc_index} ${playbook_item}`;
const plot = new DataPlot(
shadowRoot.getElementById("plot"),
shadowRoot.getElementById("table"),
shadowRoot.getElementById("measurement"),
heading);
this.plot = plot;
adc_update_listeners[`${playbook_item}_${adc_index}`] = (mean, variance) => {
plot.update_plot(mean, variance);
};
}
});
document.querySelector('#download_form').addEventListener('submit', (evt) => {
evt.preventDefault();
document.querySelector('#download-button').addEventListener('click', (evt) => {
let a = document.createElement('a');
let spec_inp = document.querySelector('#dl_meta_specimen');
const meta = document.querySelector('#dl_meta_info').value;
const suffix = document.querySelector('input[name="meta_suffix"]:checked').value;
adc1_plot.lock = true;
adc2_plot.lock = true;
const specimen = spec_inp.value;
const mcu = document.querySelector('#dl_meta_mcu').value;
const chip = document.querySelector('#dl_meta_chip').value;
const timestamp = (new Date).toISOString();
spec_inp.value = "";
a.setAttribute('href', 'data:application/json;charset=utf-8,' + JSON.stringify({
meta: meta + suffix,
meta: `${meta}_${mcu}_${chip}_${specimen}`,
info: {
meta: meta,
specimen: specimen,
mcu: mcu,
chip: chip,
timestamp: timestamp,
},
settings: dbg_settings(),
adc1: adc1_plot.adc_data,
var1: adc1_plot.var_data,
adc2: adc2_plot.adc_data,
var2: adc2_plot.var_data,
data: last_data,
}));
adc1_plot.lock = false;
adc2_plot.lock = false;
a.setAttribute('download', 'adc-capture-' + (new Date).toISOString() + '.json');
a.setAttribute('download', 'multisample-' + timestamp + '.json');
a.style.display = 'none';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
});
let last_data = null;
const socket = io();
socket.on('graph_update', (arg, callback) => {
adc1_plot.update_plot(arg['adc1'], arg['var1']);
adc2_plot.update_plot(arg['adc2'], arg['var2']);
last_data = structuredClone(arg['data']);
document.getElementById('dl_meta_mcu').value = arg['mcu_serial'];
document.getElementById('dl_meta_chip').value = arg['chip_type'];
for (const [playbook_item, value] of Object.entries(arg['data'])) {
nop = (a, b) => null;
(adc_update_listeners[`${playbook_item}_1`] || nop)(value['adc1'], value['var1']);
(adc_update_listeners[`${playbook_item}_2`] || nop)(value['adc2'], value['var2']);
}
});
function dbg_settings() {

@ -1 +1 @@
Subproject commit 5ff70236318a1e48bcc89a6174b243c7443ae1c1
Subproject commit e2f4fa6e7975baa3dc4256c4d64c81a1c191675b