272 self.setWindowTitle(
"ADC Plotter - Simple Test")
273 self.resize(1280, 740)
280 central = QtWidgets.QWidget()
281 self.setCentralWidget(central)
282 root = QtWidgets.QVBoxLayout(central)
285 top = QtWidgets.QHBoxLayout()
286 top.addWidget(QtWidgets.QLabel(
"Port"))
291 top.addWidget(QtWidgets.QLabel(
"Baud"))
296 for b
in [9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600]
299 self.
cmb_baud.setCurrentText(str(BAUD))
302 btn_refresh = QtWidgets.QPushButton(
"Refresh")
304 top.addWidget(btn_refresh)
310 btn_settings = QtWidgets.QPushButton(
"Settings")
312 top.addWidget(btn_settings)
314 btn_console = QtWidgets.QPushButton(
"Console")
316 top.addWidget(btn_console)
318 btn_export = QtWidgets.QPushButton(
"Download as CSV")
320 top.addWidget(btn_export)
331 main = QtWidgets.QHBoxLayout()
332 root.addLayout(main, 1)
336 self.
plot.setLabel(
"bottom",
"Time", units=
"s")
337 self.
plot.setLabel(
"left",
"Voltage", units=
"V")
338 self.
plot.showGrid(x=
True, y=
True, alpha=0.3)
339 self.
plot.setTitle(
"ADC Readings - Connectors 1, 2, 3")
340 self.
plot.setClipToView(
True)
341 self.
plot.setDownsampling(mode=
"peak")
342 self.
plot.setMouseEnabled(x=
True, y=
True)
344 main.addWidget(self.
plot, 1)
347 right = QtWidgets.QVBoxLayout()
348 main.addLayout(right)
351 right.addWidget(QtWidgets.QLabel(
"Show channels:"))
354 for ch
in range(NUM_CHANNELS):
355 cb = QtWidgets.QCheckBox(CHANNEL_NAMES[ch])
360 color = CHANNEL_COLORS[ch]
361 pen = pg.mkPen(color=color, width=2)
362 self.
curves[ch] = self.
plot.
plot([], [], name=CHANNEL_NAMES[ch], pen=pen)
365 box = QtWidgets.QGroupBox(
"Statistics")
366 form = QtWidgets.QVBoxLayout(box)
371 form.addWidget(self.
_hline())
372 form.addWidget(QtWidgets.QLabel(
"Mean voltage (last 100 ms):"))
375 for ch
in range(NUM_CHANNELS):
376 lbl = QtWidgets.QLabel(f
"{CHANNEL_NAMES[ch]}: n/a")
382 status_box = QtWidgets.QGroupBox(
"Status")
383 status_layout = QtWidgets.QVBoxLayout(status_box)
387 right.addWidget(status_box)
395 self.
t = {ch: deque(maxlen=MAX_POINTS)
for ch
in range(NUM_CHANNELS)}
396 self.
v = {ch: deque(maxlen=MAX_POINTS)
for ch
in range(NUM_CHANNELS)}
505 has_data = any(len(self.
t.get(ch, [])) > 0
for ch
in range(NUM_CHANNELS))
512 for ch, cb
in self.
chk.items():
514 t_data = self.
t.get(ch, [])
515 v_data = self.
v.get(ch, [])
516 if t_data
and v_data:
518 self.
curves[ch].setData(list(t_data), list(v_data))
520 self.
curves[ch].setData([], [])
522 self.
curves[ch].setData([], [])
526 for ch
in range(NUM_CHANNELS):
527 if self.
chk[ch].isChecked()
and self.
t.get(ch):
528 latest =
max(latest, self.
t[ch][-1])
532 self.
plot.setXRange(xmin,
max(xmin + 1e-3, latest), padding=0)
537 for ch
in range(NUM_CHANNELS):
538 if self.
chk[ch].isChecked():
539 v_data = self.
v.get(ch, [])
541 values.extend(v_data)
543 vmin, vmax =
min(values),
max(values)
545 pad = 0.1
if vmax == 0
else abs(vmax) * 0.1
546 self.
plot.setYRange(vmin - pad, vmax + pad, padding=0)
550 vmin - 0.1 * rng, vmax + 0.1 * rng, padding=0
553 self.
plot.setYRange(0.0, float(self.
max_v) + 0.1, padding=0)
556 now = time.monotonic()
561 sps = samples_delta / elapsed
563 pts = [len(self.
t[ch])
for ch
in range(NUM_CHANNELS)]
565 f
"SPS: {sps:.1f} | Pkts: {self.packets_received} | Pts: {pts}"
572 for ch
in range(NUM_CHANNELS):
573 if not self.
chk[ch].isChecked():
574 self.
per_ch[ch].setText(f
"{CHANNEL_NAMES[ch]}: n/a")
577 ts = self.
t.get(ch, [])
578 vs = self.
v.get(ch, [])
580 cutoff = latest - 0.1
583 for i
in range(len(ts) - 1, -1, -1):
589 mean_v = sum(recent) / len(recent)
590 self.
per_ch[ch].setText(f
"{CHANNEL_NAMES[ch]}: {mean_v:.4f} V")
592 self.
per_ch[ch].setText(f
"{CHANNEL_NAMES[ch]}: n/a")
594 self.
per_ch[ch].setText(f
"{CHANNEL_NAMES[ch]}: n/a")
597 for ch
in range(NUM_CHANNELS):
598 self.
per_ch[ch].setText(f
"{CHANNEL_NAMES[ch]}: n/a")
617 """Export current data to CSV file"""
619 total_points = sum(len(self.
t[ch])
for ch
in range(NUM_CHANNELS))
620 if total_points == 0:
621 QtWidgets.QMessageBox.warning(
622 self,
"No Data",
"No data available to export."
630 timestamp = datetime.now().strftime(
"%Y%m%d_%H%M%S")
631 default_filename = f
"adc_data_{timestamp}.csv"
634 filename, _ = QtWidgets.QFileDialog.getSaveFileName(
635 self,
"Save CSV File", default_filename,
"CSV Files (*.csv);;All Files (*)"
642 with open(filename,
"w", newline=
"", encoding=
"utf-8")
as csvfile:
643 writer = csv.writer(csvfile)
646 header = [
"Time (s)"]
647 for ch
in range(NUM_CHANNELS):
648 header.append(f
"{CHANNEL_NAMES[ch]} (V)")
649 writer.writerow(header)
653 time_ref = list(self.
t[0])
if len(self.
t[0]) > 0
else []
654 max_points =
max(len(self.
t[ch])
for ch
in range(NUM_CHANNELS))
657 for i
in range(max_points):
661 if i < len(time_ref):
662 row.append(f
"{time_ref[i]:.6f}")
667 for ch
in range(NUM_CHANNELS):
668 if i < len(self.
v[ch]):
669 row.append(f
"{self.v[ch][i]:.6f}")
676 points_per_channel = [len(self.
t[ch])
for ch
in range(NUM_CHANNELS)]
677 QtWidgets.QMessageBox.information(
680 f
"Data exported successfully to:\n{filename}\n\n"
681 f
"Points per channel: {points_per_channel}",
683 except Exception
as e:
684 QtWidgets.QMessageBox.critical(
685 self,
"Export Failed", f
"Failed to export data:\n{str(e)}"