Skip to content

Commit

Permalink
Add time averaged photon counter
Browse files Browse the repository at this point in the history
  • Loading branch information
lange50 committed Mar 28, 2025
1 parent 2020829 commit 7ba0e37
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 2 deletions.
9 changes: 8 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: [terascan_gui]
startup_modules: [photon_counts_time_average_gui, terascan_gui]

# Module server configuration for accessing qudi GUI/logic/hardware modules from remote clients
remote_modules_server:
Expand Down Expand Up @@ -38,6 +38,13 @@ gui:
connect:
terascan_logic: terascan_logic

photon_counts_time_average_gui:
module.Class: 'swabian.photon_counts_time_average_gui.PhotonCountsTimeAverageGui'
connect:
swabian_timetagger: 'swabian_timetagger'
options:
ring_buffer_length_s: 5

logic:
terascan_logic:
module.Class: 'terascan_logic.TerascanLogic'
Expand Down
93 changes: 93 additions & 0 deletions src/qudi/gui/swabian/photon_counts_time_average_gui.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# -*- coding: utf-8 -*-

__all__ = ['PhotonCountsTimeAverageGui']

import numpy as np
import os
from PySide2 import QtCore, QtGui
from typing import List
from time import sleep
from collections import deque

from qudi.util.datastorage import TextDataStorage
from qudi.core.module import GuiBase
from qudi.core.connector import Connector
from qudi.core.statusvariable import StatusVar
from qudi.core.configoption import ConfigOption
from qudi.gui.swabian.photon_counts_time_average_main_window import PhotonCountsTimeAverageMainWindow
from qudi.util.paths import get_artwork_dir

from qudi.logic.terascan_logic import TerascanData

# TODO: put the maxlen in the config file
class PhotonCountsTimeAverageGui(GuiBase):
""" Photon Counting Time Average GUI
example config for copy-paste:
photon_counts_time_average_gui:
module.Class: 'swabian.photon_counts_time_average_gui.PhotonCountsTimeAverageGui'
connect:
swabian_timetagger: 'swabian_timetagger'
options:
ring_buffer_length_s: 10
"""
# One logic module
_ring_buffer_length_s = ConfigOption(name='ring_buffer_length_s', default=10, missing='warn')
_photon_counts_logic = Connector(name='swabian_timetagger', interface='FastCounterInterface')

def on_activate(self) -> None:
self._data = deque(maxlen=1000 * self._ring_buffer_length_s)

# initialize the main window
self._mw = PhotonCountsTimeAverageMainWindow()

# Signals from GUI:
self._mw.start_button.clicked.connect(self._photon_counts_logic().start_measure)

# Connect signals from logic modules
# The first function is a QtCore.Slot
self._photon_counts_logic().sigScanFinished.connect(
self._counts_changed, QtCore.Qt.QueuedConnection
)

# Turn on update timer:
self.__timer = QtCore.QTimer()
self.__timer.setSingleShot(False) # False means that the timer will repeat
self.__timer.timeout.connect(self.__update_plot)
self.__timer.start(250) # 250 ms

self.show()


def on_deactivate(self) -> None:
# When you call a connector, you should do it as a function, as shown here. Noone knows why.
# For some reason, when connecting to an external connector, you also need to specify which function you are disconnecting
self._photon_counts_logic().sigPhotonCounts.disconnect(self._counts_changed)

self._mw.start_button.clicked.disconnect()

# disable update timer:
self.__timer.stop()
self.__timer.timeout.disconnect()
self.__timer = None

self._mw.close()

def show(self) -> None:
""" Mandatory method to show the main window """
self._mw.show()
self._mw.raise_()

@QtCore.Slot(np.ndarray)
def _counts_changed(self, counts: np.ndarray) -> None:
self._data.append(np.sum(counts))

def __update_plot(self) -> None:
if (len(self._data) == 0):
return

x = range(len(self._data))
y = list(self._data)


self._mw.data_item.setData(x = x, y = y)
69 changes: 69 additions & 0 deletions src/qudi/gui/swabian/photon_counts_time_average_main_window.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# -*- coding: utf-8 -*-

__all__ = ['PhotonCountsTimeAverageMainWindow']

import os # imported for potential future use (e.g., loading icons or other assets)
from PySide2 import QtGui, QtCore, QtWidgets
import pyqtgraph as pg

# Although these imports are not used in the current code, they might be needed for extended functionality.
from qudi.util.widgets.plotting.image_widget import ImageWidget
from qudi.util.paths import get_artwork_dir
from qudi.util.colordefs import QudiPalettePale as palette
from qudi.hardware.laser.solstis_constants import * # wildcard import used per Qudi convention

class PhotonCountsTimeAverageMainWindow(QtWidgets.QMainWindow):
"""
Main window for displaying time-averaged photon counts.
This window contains a live-updating plot using pyqtgraph that displays photon counts on the y-axis
versus time (in seconds) on the x-axis. The current implementation sets up the plot layout and visual style.
Additional widgets, such as an LCD display for the live number, are provided as commented code for future use.
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Set the window title to reflect its function
self.setWindowTitle('Time Averaged Photon Counts')

# Create the plot widget using pyqtgraph
self.plot_widget = pg.PlotWidget()
# Set minimal margins for the plot area
self.plot_widget.getPlotItem().setContentsMargins(1, 1, 1, 1)
# Ensure the widget expands with the window
self.plot_widget.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
QtWidgets.QSizePolicy.Expanding)
# Prevent the plot widget from receiving focus
self.plot_widget.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus)
# Set axis labels
self.plot_widget.setLabel('bottom', text='Time', units='s')
self.plot_widget.setLabel('left', text='Counts')

# Create a PlotDataItem for displaying data points on the plot
self.data_item = pg.PlotDataItem(
pen=pg.mkPen(palette.c1, style=QtCore.Qt.DotLine),
symbol='o',
symbolPen=palette.c1,
symbolBrush=palette.c1,
symbolSize=7
)
self.plot_widget.addItem(self.data_item)

self.start_button = QtWidgets.QPushButton('Start')

# Optional: An LCD widget to display the current photon count as a number.
# Uncomment and adjust if a numerical display is required.
# self.lcd = QtWidgets.QLCDNumber()
# self.lcd.setDigitCount(5)

# Arrange widgets in a grid layout
layout = QtWidgets.QGridLayout()
# Place the plot widget spanning 4 rows and 4 columns
layout.addWidget(self.plot_widget, 0, 0, 4, 4)
layout.addWidget(self.start_button, 4, 5, 1, 1)
# Set column stretch to ensure proper scaling
layout.setColumnStretch(1, 1)

# Create a central widget, set the layout, and then assign it as the main window's central widget
central_widget = QtWidgets.QWidget()
central_widget.setLayout(layout)
self.setCentralWidget(central_widget)
7 changes: 6 additions & 1 deletion src/qudi/gui/terascan/terascan_gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
class TerascanGui(GuiBase):
""" Terascan Measurement GUI
exaple config for copy-paste:
example config for copy-paste:
terascan_gui:
module.Class: 'terascan.terascan_gui.TerascanGui'
connect:
Expand Down Expand Up @@ -246,10 +246,15 @@ def _update_ui(self, running: bool) -> None:
self._mw.start_stop_button.setText('Start Measurement')
self._mw._statusbar.showMessage('Ready')

# TODO: Make this more efficient. There is no reason to redraw the entire plot each time.
def _update_plot(self) -> None:
if (len(self._data) == 0):
return


# x, y = self._mw.data_item.getData()
# new_count = len(self._data) - len(x)

img = np.zeros((len(self._data),2))
for i in range(len(self._data) - 1):
img[i][0] = self._data[i].wavelength*1e-3
Expand Down
1 change: 1 addition & 0 deletions src/qudi/logic/terascan_logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ def start_scan(self):
self._current_data = []
self._last_locked = time.time()
self.sigSetLaserWavelengths.emit(self._start_wavelength, self._end_wavelength)
self.sigStopCounting.emit() # In case we are counting from somewhere else
self.sigStartScan.emit()

@QtCore.Slot()
Expand Down

0 comments on commit 7ba0e37

Please sign in to comment.