Skip to content

Add powermeter #4

Merged
merged 2 commits into from
Apr 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion cfg/terascan.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
global:
# list of modules to load when starting
startup_modules: [photon_counts_time_average_gui, terascan_gui]
startup_modules: [photon_counts_time_average_gui, powermeter_gui, terascan_gui]

# Module server configuration for accessing qudi GUI/logic/hardware modules from remote clients
remote_modules_server:
Expand Down Expand Up @@ -44,6 +44,11 @@ gui:
swabian_timetagger: 'swabian_timetagger'
options:
ring_buffer_length_s: 5

powermeter_gui:
module.Class: 'powermeter.powermeter_gui.PowermeterGui'
connect:
powermeter_logic: powermeter_logic

logic:
terascan_logic:
Expand All @@ -59,6 +64,11 @@ logic:
mode_hop_overlap_med: 0.001 # in nm, from the SolsTiS control panel. This is how far back we go to discard data every time a mode hop occurs
mode_hop_overlap_fine: 0.00025 # ""

powermeter_logic:
module.Class: 'powermeter.powermeter_logic.PowermeterLogic'
connect:
powermeter: powermeter_thorlabs

# daq_reader_logic:
# module.Class: 'common.daq_reader_logic.DAQReaderLogic'
# connect:
Expand Down Expand Up @@ -117,6 +127,12 @@ hardware:
host_ip_addr: '192.168.1.225' # IP address of control computer
laser_ip_addr: '192.168.1.222' # IP address of laser
laser_port: 39900 # Port number to connect on

powermeter_thorlabs:
module.Class: 'powermeter.thorlabs_power_meter.ThorlabsPowerMeter'
options:
average_count: 5
update_period: 100 # in ms, hardware will emit data ~10 times/second

# Dummy Hardware:
# daq_reader_dummy:
Expand Down
53 changes: 53 additions & 0 deletions src/qudi/gui/powermeter/powermeter_gui.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from PySide2 import QtCore
from qudi.core.module import GuiBase
from qudi.core.connector import Connector
from qudi.core.statusvariable import StatusVar

from .powermeter_main_window import PowermeterMainWindow

class PowermeterGui(GuiBase):
"""
GUI module to display power meter readings with a multiplication factor.
Example config:
powermeter_gui:
module.Class: 'powermeter.powermeter_gui.PowermeterGui'
connect:
powermeter_logic: powermeter_logic
"""
# Connector to the logic module (which emits power readings)
_powermeter_logic = Connector(name='powermeter_logic', interface='PowermeterLogic')
# StatusVar to store the multiplication factor between sessions
_multiplication_factor = StatusVar(name='multiplication_factor', default=1.0)

def on_activate(self):
self._mw = PowermeterMainWindow()
# Initialize the main window's multiplication factor from the status variable.
self._mw.set_multiplication_factor(self._multiplication_factor)
# Set the spinbox value accordingly.
self._mw.mult_factor_spinbox.setValue(self._multiplication_factor)
# Connect spinbox changes to our handler.
self._mw.mult_factor_spinbox.valueChanged.connect(self._on_mult_factor_changed)
# Connect logic's power update signal to the main window update slot.
self._powermeter_logic().sigPowerUpdated.connect(
self._mw.update_power, QtCore.Qt.QueuedConnection
)
self.show()

def on_deactivate(self):
self._powermeter_logic().sigPowerUpdated.disconnect(self._mw.update_power)
self._mw.mult_factor_spinbox.valueChanged.disconnect(self._on_mult_factor_changed)
self._mw.close()

def show(self):
self._mw.show()
self._mw.raise_()

@QtCore.Slot(float)
def _on_mult_factor_changed(self, factor: float):
"""
Slot called when the multiplication factor is changed via the spinbox.
Updates the StatusVar and informs the main window.
"""
self._multiplication_factor = factor
self._mw.set_multiplication_factor(factor)
102 changes: 102 additions & 0 deletions src/qudi/gui/powermeter/powermeter_main_window.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
from PySide2 import QtWidgets, QtCore, QtGui

class PowermeterMainWindow(QtWidgets.QMainWindow):
"""
Main window for displaying the Thorlabs Power Meter reading.
Features:
- Automatic unit conversion.
- A multiplication factor control (for correcting losses) in the lower right,
where the user types in the value (no increment arrows).
- Dynamic font scaling: as the window is resized, the power reading text scales.
"""
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle('Thorlabs Power Meter')
self.resize(400, 180)

# Initialize multiplication factor (default 1.0)
self._mult_factor = 1.0

# Set up central widget and main layout
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
main_layout = QtWidgets.QVBoxLayout(central_widget)
main_layout.setContentsMargins(10, 10, 10, 10)
main_layout.setSpacing(5)

# Create power reading label with initial font
self.power_label = QtWidgets.QLabel("--", self)
self.power_label.setAlignment(QtCore.Qt.AlignCenter)
initial_font = QtGui.QFont("Arial", 24)
self.power_label.setFont(initial_font)
main_layout.addWidget(self.power_label)

# Add a stretch to push the following controls to the bottom
main_layout.addStretch()

# Create a horizontal layout for the multiplication factor controls
h_layout = QtWidgets.QHBoxLayout()
h_layout.addStretch() # Pushes items to the right side

self.mult_factor_label = QtWidgets.QLabel("Multiplication Factor:", self)
self.mult_factor_spinbox = QtWidgets.QDoubleSpinBox(self)
self.mult_factor_spinbox.setDecimals(4)
self.mult_factor_spinbox.setRange(0.0, 1000.0)
self.mult_factor_spinbox.setSingleStep(0.1)
# Remove the up/down arrows so the user types the value
self.mult_factor_spinbox.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)
self.mult_factor_spinbox.setValue(self._mult_factor)

h_layout.addWidget(self.mult_factor_label)
h_layout.addWidget(self.mult_factor_spinbox)
main_layout.addLayout(h_layout)

@QtCore.Slot(float)
def update_power(self, power: float):
"""
Update the label text with the new power reading.
The raw power (in Watts) is multiplied by the multiplication factor, then
converted into a suitable unit (pW, nW, µW, mW, or W).
"""
corrected_power = power * self._mult_factor
abs_pwr = abs(corrected_power)

if abs_pwr < 1e-12:
display_val = corrected_power
display_unit = "W"
elif abs_pwr < 1e-9:
display_val = corrected_power * 1e12
display_unit = "pW"
elif abs_pwr < 1e-6:
display_val = corrected_power * 1e9
display_unit = "nW"
elif abs_pwr < 1e-3:
display_val = corrected_power * 1e6
display_unit = "µW"
elif abs_pwr < 1:
display_val = corrected_power * 1e3
display_unit = "mW"
else:
display_val = corrected_power
display_unit = "W"

self.power_label.setText(f"{display_val:.4f} {display_unit}")

def set_multiplication_factor(self, factor: float):
"""
Update the multiplication factor.
"""
self._mult_factor = factor

def resizeEvent(self, event):
"""
Dynamically scale the font size of the power reading label based on the
available width of the central widget.
Adjust the divisor (here, 10) to fine-tune scaling.
"""
available_width = self.centralWidget().width()
new_font_size = max(12, int(available_width / 8))
font = self.power_label.font()
font.setPointSize(new_font_size)
self.power_label.setFont(font)
super().resizeEvent(event)
Loading