Remove nonessential files

- photorec.ses / photorec.se2: accidentally committed PhotoRec
  data-recovery session files
- sc3-aui-focusing.py: dead module — imports a T3RStepperDriver that
  doesn't exist anywhere; superseded by t3r_control_panel.py
- adc_bug.md: stale debugging note for an already-applied fix
- app_style.qss: empty stylesheet; app.py already handles its absence

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Thomas Ales
2026-07-28 09:27:30 -05:00
parent 2041fc8439
commit 7bcdff9756
5 changed files with 0 additions and 56742 deletions
-37
View File
@@ -1,37 +0,0 @@
# ADC YOFF Sign Bug — sras_viewer.py
## Status
Fix applied, awaiting user testing.
## What was wrong
`DC_YOFF_ADC` in `sras_viewer.py` was `+87.04` instead of `-87.04`.
The Tektronix scope stores CH3/CH4 waveform data as **signed int8** (−128 to +127), where ADC 0 = screen center. The scope's vertical position for CH3/CH4 is set to `−2.72 div` in `sc3_aui_app.py`, which places 0 V **below** center at ADC count `−2.72 × 32 = −87.04`. The comment in the code had the formula as `-position × (256/8)` (sign flipped), producing `+87.04` instead of the correct `−87.04`.
## Effect of the bug
- `adc_to_mv` was off by 272 mV in the negative direction
- ADC −87 (true 0 V signal) → −272 mV (should be ≈ 0 mV)
- ADC 0 (screen center, above ground) → −136 mV (should be +136 mV)
- DC images for CH3/CH4 (Bias A/B) showed large negative voltages, physically impossible for DC bias signals
- RF mask threshold (`mv_to_adc`) was also broken: threshold ADC value ~+87 was being compared against pixel means clustered around −87, so nearly every pixel would have been incorrectly masked
## The fix
`sras_viewer.py` line 48:
```python
# Before
DC_YOFF_ADC = 87.04 # ADC count that represents 0 V
# After
DC_YOFF_ADC = -87.04 # ADC count that represents 0 V
```
Comment on line 47 also corrected from `-position × (256/8)` to `position × (256/8)`.
## What to verify during testing
1. CH3 and CH4 DC images show positive (or near-zero) voltages consistent with the bias signal levels
2. RF (CH1) image is not excessively masked — pixels with a genuine bias signal above the threshold should appear
3. `mv_to_adc(0.0)` should now return −87.04 (not +87.04)
4. The default threshold of 0.125 mV should correspond to ADC ≈ −87.0, not +87.1
View File
BIN
View File
Binary file not shown.
-56298
View File
File diff suppressed because one or more lines are too long
-407
View File
@@ -1,407 +0,0 @@
"""Focusing Control Panel — Qt UI module for ScanEngine-3.
This module provides a dialog-based control panel for the T3R stepper
controller, allowing users to home, step, jog, and monitor the focus
axis in real time. It integrates with scanengine via the hardware
abstraction layer (hardware.t3r_driver).
Usage:
from sc3_aui_focusing import FocusingControlPanel
panel = FocusingControlPanel(parent_window)
panel.show()
Signals:
focus_position_changed(ch, position) — emitted after each move/jog completes
focus_error(ch, message) — emitted on controller errors
"""
from __future__ import annotations
import logging
from typing import Optional
try:
from PyQt6.QtWidgets import (
QDialog, QDoubleSpinBox, QPushButton, QLabel, QGroupBox, QVBoxLayout,
QHBoxLayout, QSpacerItem, QSizePolicy, QErrorMessage, QMessageBox,
)
from PyQt6.QtCore import Qt, pyqtSignal
except ImportError:
from PyQt5.QtWidgets import (
QDialog, QDoubleSpinBox, QPushButton, QLabel, QGroupBox, QVBoxLayout,
QHBoxLayout, QSpacerItem, QSizePolicy, QErrorMessage, QMessageBox,
)
from PyQt5.QtCore import Qt, pyqtSignal
from hardware import T3RStepperDriver
logger = logging.getLogger(__name__)
class FocusingControlPanel(QDialog):
"""Qt dialog for controlling the T3R focus stepper."""
focus_position_changed = pyqtSignal(int, float)
focus_error = pyqtSignal(int, str)
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("Focusing Control Panel")
self.setWindowFlags(Qt.Window | Qt.WindowCloseButtonHint)
self.setMinimumWidth(780)
self.setMinimumHeight(560)
self._driver = None
self._connected = False
self._build_ui()
def _build_ui(self):
main_layout = QVBoxLayout(self)
main_layout.setContentsMargins(20, 20, 20, 20)
main_layout.setSpacing(10)
title_label = QLabel("Focusing Control Panel")
title_label.setFont(title_label.font().copy(size=Qt.FontSize.Fixed))
title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
main_layout.addWidget(title_label)
status_groupbox = QGroupBox("Status")
status_layout = QHBoxLayout(status_groupbox)
self.status_label = QLabel("Not connected")
self.status_label.setMinimumWidth(200)
status_layout.addWidget(self.status_label)
main_layout.addWidget(status_groupbox)
axis_groupbox = QGroupBox("T-axis (Focus)")
axis_layout = QHBoxLayout(axis_groupbox)
self.position_label = QLabel("Position: 0")
self.position_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
axis_layout.addWidget(self.position_label)
main_layout.addWidget(axis_groupbox)
controls_groupbox = QGroupBox("Controls")
controls_layout = QVBoxLayout(controls_groupbox)
controls_layout.setSpacing(6)
home_row = QHBoxLayout()
self.home_button = QPushButton("Home")
home_row.addWidget(self.home_button)
spacer = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
home_row.addSpacerItem(spacer)
controls_layout.addLayout(home_row)
step_row = QHBoxLayout()
self.step_minus_button = QPushButton("< Step (-)")
self.steps_spinbox = QDoubleSpinBox()
self.steps_spinbox.setMinimum(-10000.0)
self.steps_spinbox.setMaximum(10000.0)
self.steps_spinbox.setSingleStep(100.0)
self.step_plus_button = QPushButton("Step (+) >")
step_row.addWidget(self.step_minus_button)
step_row.addWidget(self.steps_spinbox)
step_row.addWidget(self.step_plus_button)
controls_layout.addLayout(step_row)
jog_row = QHBoxLayout()
self.jog_minus_button = QPushButton("< Jog (-)")
self.velocity_spinbox = QDoubleSpinBox()
self.velocity_spinbox.setMinimum(-10000.0)
self.velocity_spinbox.setMaximum(10000.0)
self.velocity_spinbox.setSingleStep(500.0)
self.jog_plus_button = QPushButton("Jog (+) >")
jog_row.addWidget(self.jog_minus_button)
jog_row.addWidget(self.velocity_spinbox)
jog_row.addWidget(self.jog_plus_button)
controls_layout.addLayout(jog_row)
accel_row = QHBoxLayout()
self.accel_minus_button = QPushButton("< Accel (-)")
self.accel_spinbox = QDoubleSpinBox()
def connect(self, port="/dev/ttyUSB0", spd=115200):
"""Connect to the T3R controller and start polling."""
try:
self._driver = T3RStepperDriver()
self._driver.connect(port=port, spd=spd)
self._connected = True
self.status_label.setText("Connected")
self.position_label.setText(f"Position: {self._driver.get_position(0):,.0f}")
return True
except Exception as e:
logger.exception("Failed to connect to T3R controller")
self.status_label.setText(f"Connection error: {e!s}")
return False
def disconnect(self):
"""Disconnect from the controller."""
if self._driver is not None:
try:
self._driver.disconnect()
except Exception as e:
logger.warning("Error disconnecting: %s", e)
self._driver = None
self._connected = False
self.status_label.setText("Not connected")
def home(self):
"""Home the T-axis."""
if not self._connected or self._driver is None:
return
try:
self._driver.move(0, steps=0, velocity=8000, accel=4000)
self.position_label.setText("Position: 0")
except Exception as e:
logger.exception("Home failed")
self.focus_error.emit(0, f"Home error: {e!s}")
def step(self, direction=1):
"""Step by the amount in steps_spinbox."""
if not self._connected or self._driver is None:
return
try:
steps = int(round(self.steps_spinbox.value()))
if steps == 0:
return
sign = 1 if direction > 0 else -1
self._driver.move(0, steps=sign * steps, velocity=8000, accel=4000)
pos = self._driver.get_position(0)
self.position_label.setText(f"Position: {pos:,}")
except Exception as e:
logger.exception("Step failed")
self.focus_error.emit(0, f"Step error: {e!s}")
def jog(self, direction=1):
"""Jog at the velocity in velocity_spinbox."""
if not self._connected or self._driver is None:
return
try:
vel = int(round(self.velocity_spinbox.value()))
if vel == 0:
return
sign = 1 if direction > 0 else -1
accel = int(round(self.accel_spinbox.value()))
self._driver.jog(0, velocity=sign * vel, accel=accel)
except Exception as e:
logger.exception("Jog failed")
self.focus_error.emit(0, f"Jog error: {e!s}")
def stop(self):
"""Stop any motion."""
if not self._connected or self._driver is None:
return
try:
self._driver.stop(0)
except Exception as e:
logger.exception("Stop failed")
self.focus_error.emit(0, f"Stop error: {e!s}")
def refresh_position(self):
"""Update the position label (called from polling thread)."""
if not self._connected or self._driver is None:
return
try:
pos = self._driver.get_position(0)
self.position_label.setText(f"Position: {pos:,}")
except Exception:
pass
# ── Polling integration ──────────────────────────────────────────────────
def start_polling(self, interval=0.3):
"""Start periodic position polling (called after connect)."""
if self._driver is None:
return
self._driver.start_polling(interval)
def stop_polling(self):
"""Stop polling."""
if self._driver is not None:
self._driver.stop_polling()
def poll_loop(self):
"""Run the polling loop (typically in a separate thread)."""
self.refresh_position()
import time
while self._connected and self._driver is not None:
try:
self._driver.start_polling(0.3)
except Exception:
pass
time.sleep(0.5)
# ── Signal handlers ──────────────────────────────────────────────────────
def _on_home_clicked(self):
self.home()
def _on_step_minus_clicked(self):
self.step(-1)
def _on_step_plus_clicked(self):
self.step(+1)
def _on_jog_minus_clicked(self):
self.jog(-1)
def _on_jog_plus_clicked(self):
self.jog(+1)
def _on_accel_minus_clicked(self):
pass # accel is just a jog parameter, handled by jog()
def _on_accel_plus_clicked(self):
pass
def _on_stop_clicked(self):
self.stop()
def _on_close_clicked(self):
self.close()
# Wire up all buttons to their handlers
self.home_button.clicked.connect(_on_home_clicked)
self.step_minus_button.clicked.connect(_on_step_minus_clicked)
self.step_plus_button.clicked.connect(_on_step_plus_clicked)
self.jog_minus_button.clicked.connect(_on_jog_minus_clicked)
self.jog_plus_button.clicked.connect(_on_jog_plus_clicked)
self.accel_minus_button.clicked.connect(_on_accel_minus_clicked)
self.accel_plus_button.clicked.connect(_on_accel_plus_clicked)
self.stop_button.clicked.connect(_on_stop_clicked)
self.close_button.clicked.connect(_on_close_clicked)
# ── Signal handlers ──────────────────────────────────────────────────────
def _on_home_clicked(self):
self.home()
def _on_step_minus_clicked(self):
self.step(-1)
def _on_step_plus_clicked(self):
self.step(+1)
def _on_jog_minus_clicked(self):
self.jog(-1)
def _on_jog_plus_clicked(self):
self.jog(+1)
def _on_accel_minus_clicked(self):
pass # accel is just a jog parameter, handled by jog()
def _on_accel_plus_clicked(self):
pass
def _on_stop_clicked(self):
self.stop()
def _on_close_clicked(self):
self.close()
# Wire up all buttons to their handlers
self.home_button.clicked.connect(_on_home_clicked)
self.step_minus_button.clicked.connect(_on_step_minus_clicked)
self.step_plus_button.clicked.connect(_on_step_plus_clicked)
self.jog_minus_button.clicked.connect(_on_jog_minus_clicked)
self.jog_plus_button.clicked.connect(_on_jog_plus_clicked)
self.accel_minus_button.clicked.connect(_on_accel_minus_clicked)
self.accel_plus_button.clicked.connect(_on_accel_plus_clicked)
self.stop_button.clicked.connect(_on_stop_clicked)
self.close_button.clicked.connect(_on_close_clicked)
def closeEvent(self, event):
"""Save state and cleanup."""
self.disconnect()
# ── Polling integration ──────────────────────────────────────────────────
def start_polling(self, interval=0.3):
"""Start periodic position polling (called after connect)."""
if self._driver is None:
return
self._driver.start_polling(interval)
def stop_polling(self):
"""Stop polling."""
if self._driver is not None:
self._driver.stop_polling()
def poll_loop(self):
"""Run the polling loop (typically in a separate thread)."""
self.refresh_position()
import time
while self._connected and self._driver is not None:
try:
self._driver.start_polling(0.3)
except Exception:
pass
time.sleep(0.5)
# ── Module documentation ───────────────────────────────────────────────────
__all__ = ["FocusingControlPanel"]
if __name__ == "__main__":
import sys
from PyQt6.QtWidgets import QApplication
app = QApplication(sys.argv)
panel = FocusingControlPanel()
panel.show()
sys.exit(app.exec())
# ── Polling integration ──────────────────────────────────────────────────
def start_polling(self, interval=0.3):
"""Start periodic position polling (called after connect)."""
if self._driver is None:
return
self._driver.start_polling(interval)
def stop_polling(self):
"""Stop polling."""
if self._driver is not None:
self._driver.stop_polling()
def poll_loop(self):
"""Run the polling loop (typically in a separate thread)."""
self.refresh_position()
import time
while self._connected and self._driver is not None:
try:
self._driver.start_polling(0.3)
except Exception:
pass
time.sleep(0.5)
event.accept()
self.accel_spinbox.setMinimum(-10000.0)
self.accel_spinbox.setMaximum(10000.0)
self.accel_spinbox.setSingleStep(500.0)
self.accel_plus_button = QPushButton("Accel (+) >")
accel_row.addWidget(self.accel_minus_button)
accel_row.addWidget(self.accel_spinbox)
accel_row.addWidget(self.accel_plus_button)
controls_layout.addLayout(accel_row)
stop_row = QHBoxLayout()
self.stop_button = QPushButton("Stop")
stop_row.addWidget(self.stop_button)
controls_layout.addLayout(stop_row)
main_layout.addWidget(controls_groupbox)
self.close_button = QPushButton("Close")
main_layout.addWidget(self.close_button)
# ── Module documentation ───────────────────────────────────────────────────
__all__ = ["FocusingControlPanel"]
if __name__ == "__main__":
import sys
from PyQt6.QtWidgets import QApplication
app = QApplication(sys.argv)
panel = FocusingControlPanel()
panel.show()
sys.exit(app.exec())