This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Documentation

Guides for installing, programming and operating Quatm.

From core to complete

Quatm Core is the open-source runtime for experiment control. It provides the experiment lifecycle, physics models, servers and shared utilities, and runs without a desktop environment.

Quatm Platform adds the desktop GUI, browser console, driver catalogue, licensing and support for laboratories that need a complete operating environment.

Quatm Systems extends the platform into the laboratory itself: real-time controllers, lasers, high-field magnets, RF sources, integrated ion-trap and ultracold-atom systems, and complete experimental systems.

Start here

Documentation should make an experiment feel smaller, not larger. These guides follow the way a real experiment comes to life: describe the apparatus, write the sequence, observe what happens, and keep what you learn.

If this is your first visit, begin with Getting started. It asks nothing of your hardware; simulation mode uses the same experiment model as the real apparatus. From there, follow the path that matches your work:

Getting started → Experiment editing → Device drivers → Data and analysis → Application examples.

Documentation map

SectionWhat you will learn
ArchitectureServices, processes and the boundaries that keep an experiment legible
Getting startedInstallation, project layout, device configuration and a first experiment
FAQCommon installation, device, data and upgrade questions
Experiment editingThe Experiment lifecycle, parameter types and timing primitives
Experiment frameworkWidgets, queue, scheduler, preparation station, looper and timeline internals
UtilitiesShared utilities and VDS helpers
Device driversDevice modes, driver catalogue and custom driver development
Data and analysisData streams, archive structure, imaging and fitting
GUI componentsDesktop applications and their responsibilities
Web remote controlBrowser console, mobile operation and synchronisation
Communication layerMessage hubs, clients, properties and configuration
Physics modelsAtom database, optical dipole traps, Stark shifts and scattering
MonitoringEnvironmental and instrument-health monitoring
DeploymentSingle workstation, split control/analysis machines and remote access
Best practicesSafe shutdown, single responsibility and reusable components
ExamplesComplete MOT, absorption-imaging and ion-trap experiments
ContributingIssues, pull requests and review expectations

Architecture

Quatm is not a single program. Drivers, data managers, property logging, message routing, web access and the GUI can run as independent services, sharing one experiment model across a workstation or a network.

Read the architecture overview

1 - Architecture

How Quatm services, processes and clients fit together.

Distributed services

Quatm is distributed: drivers, data managers, property logging, message routing, web access and the GUI run as independent operating-system processes connected by local IPC or TCP.

flowchart TB
    subgraph Clients[Clients]
        GUI[Desktop GUI<br/>PySide6]
        WEB[Web console<br/>browser / mobile]
    end

    BUS[(ZeroMQ event bus<br/>data · images · commands · properties)]

    subgraph Control[Control]
        PM[Process & task supervision]
        EXP[Experiment runtime<br/>Experiment · scans · queue · looper]
    end

    subgraph Services[Services]
        DATA[Data & image managers]
        PROP[Property logger]
        MSG[Message hub / logger]
        HEALTH[Device health & leases]
        WEBHUB[Web hub]
    end

    subgraph Hardware[Hardware]
        DRV[Device drivers<br/>VISA · serial · TCP · USB · GPIB · DLL]
        RT[FPGA / ADwin / real-time controllers]
    end

    GUI --> BUS
    WEB --> BUS
    BUS --> PM
    BUS --> EXP
    EXP --> DATA
    EXP --> PROP
    PM --> MSG
    PM --> HEALTH
    DATA --> DRV
    EXP --> RT
    DRV --> Hardware
    WEBHUB --> WEB
    MSG --> BUS
    HEALTH --> DRV

Failure isolation and network transparency

Every major component can run as an independent operating-system process. Clients and services communicate over local IPC or TCP, so a control PC, a data-analysis machine and a remote laptop can share one experiment without sharing one process. The architecture is intentionally fail-isolated and network-transparent: image processing does not block timing, and a slow browser cannot stall acquisition.

For the deeper architecture description, see Communication layer and the repository’s quatm/docs/ directory.

2 - Getting started

Install Quatm, create a project and run a first experiment in simulation mode.

This guide takes you from a clean machine to a running experiment. You do not need any hardware: Quatm’s simulation mode exercises the same runtime and data path as a real device.

Edition note. The installation commands below set up Quatm Core, the open-source runtime. The desktop GUI, browser console and commercial driver catalogue are provided with Quatm Platform; contact sales@quatm.com for the platform distribution.

What you will do

  • Install the framework and create a laboratory project.
  • Understand the project directory and device_db.py.
  • Write a small Experiment class with parameters.
  • Run it from the desktop GUI or the web console.
  • Inspect the generated HDF5 data and analysis.

Requirements

  • Python 3.9 or newer.
  • Windows 10 (1809+) or Windows 11 on x86-64; macOS 13 or newer; Linux x86-64 (Ubuntu 22.04/24.04, Debian 12, or an equivalent environment). ARM64 desktop support is limited; consult the Qt 6.11 supported-platform matrix.
  • Optional: a supported FPGA/ADwin controller or VISA/serial/TCP/USB/GPIB instruments for real experiments.

Install

git clone https://github.com/quatm-team/quatm-core.git
cd quatm-core
python -m venv python_venv
source python_venv/bin/activate        # Windows: python_venv\Scripts\activate
pip install -e .
pip install -e ".[hardware]"           # optional hardware dependencies

Verify

from quatm.experiment import Experiment

class HelloQuatm(Experiment):
    def build(self):
        print("Quatm ready")

    def run(self):
        print("Hello, Quatm!")

HelloQuatm().run()

Create or open a project

A Quatm project contains configuration/, experiments/ and Data/. With Quatm Platform, a project normally includes a launcher; run it to open the laboratory environment.

If you start from an empty project, use the platform launcher:

python -m quatm.GUI.entries.start_quatm --root <project>

The first run creates a minimal configuration and opens the Operations Console. Open-source core users can also use the Python API directly and organise configuration/ and experiments/ in their own project.

Next steps

  1. Basic configuration — connect devices and understand the project files.
  2. Your first experiment — parameters, sequencing and execution.
  3. Experiment editing — the full runtime model.
  4. Device drivers — driver modes and custom instruments.
  5. FAQ — installation, device, data and upgrade questions.

2.1 - Basic configuration

How a project is discovered, how devices are described, and how services are configured.

Project root

Quatm resolves the project root in this order:

  1. QUATM_PROJECT_ROOT (set by the project launcher);
  2. the nearest parent directory containing configuration/configfile/configfile.json or a .quatm_project marker;
  3. the legacy repository layout.

A project keeps its own configuration and data; the framework package is installed separately. This lets several laboratories share one installation while keeping their files independent.

Directory layout

<project>/
├── configuration/
│   ├── configfile/configfile.json   # services and GUI entries to launch
│   ├── device_db.py                 # instruments used by experiments
│   └── properties/                  # persisted property tree
├── experiments/                     # experiment scripts
├── Data/                            # HDF5 acquisition files
├── drivers/                         # optional project-local drivers
├── monitor/                         # optional monitors
└── analysis/                        # optional analysis scripts

Device database

configuration/device_db.py maps logical names to drivers. An experiment refers to these names, never to vendor APIs directly.

DEVICE_DB = {
    'mot_coils': {
        'type': 'attr',
        'module': 'quatm.drivers.xxx',
        'class': 'XxxDriver',
        'address': 'tcp://192.168.1.50:5025',
        'arguments': {'channel': 1},
    },
    'probe_laser': {
        'type': 'local',
        'module': 'drivers.probe_laser',
        'class': 'ProbeLaser',
        'arguments': {'port': 'COM4'},
    },
}

The three modes are:

TypeMeaningTypical use
localInstantiate the driver as a full objectcameras, FPGA controllers, stages
attrMap one channel to a Python attributeDAC/TTL outputs
generic_attrCall an arbitrary driver functionDDS tones, multi-channel devices

A project-local module (for example drivers.probe_laser) takes precedence over a bundled driver with the same suffix. local_only / analysis_local_only can enforce that no bundled fallback is used.

Services

configuration/configfile/configfile.json declares the services and GUI programs for the project. The Operations Console reads this file, starts selected entries and tracks their state. Service processes are ordinary operating-system processes, so a crash is isolated and can be restarted independently.

Verify the setup

from quatm.servers.configreader import PROJECT_ROOT
print(PROJECT_ROOT)

Then open the Device Manager, select a device and use Test connection with simulation enabled. No hardware is required for this step.

2.2 - Your first experiment

Write, parameterise and run an experiment in simulation mode.

A minimal experiment

Put this file under the project’s experiments/ directory. Quatm discovers experiment classes from that tree.

from quatm.experiment import Experiment


class FirstExperiment(Experiment):
    def build(self):
        self.duration = self.NumberValue(100.0, unit='ms')
        self.enabled = self.BoolValue(True)
        self.phase = self.StringCombo('A', options=['A', 'B'])

    def run(self):
        if not self.enabled:
            return
        self.delay(self.duration)

build() is called once per experiment instance and declares the parameter panel. run() is called once per repetition.

Parameter types

TypePurpose
NumberValuenumeric value with optional unit, limits and step
BoolValueboolean toggle
StringComboselection from a fixed list
setattr_deviceregister an instrument for this experiment

Run it

  1. Open the Experiment Browser.
  2. Select FirstExperiment.py.
  3. Set duration and enabled in the parameter panel.
  4. Press Run.
  5. Watch the task in the Task Monitor; the Browser shows progress and results.

In simulation mode the run completes without hardware. Data, parameters and task metadata are archived to HDF5 under Data/.

Scans and repetitions

Use the browser’s scan controls to vary a parameter, set the number of repetitions, or submit a multi-dimensional scan. The framework expands the scan into individual runs and records the scan definition alongside the data.

Where to go next

2.3 - Known issues

Common problems encountered while installing and using Quatm, and their solutions.

Windows

FPGA controller driver

The vendor-provided SDK must be installed, and the FPGA requires the timetable to be defined and flashed according to the actual requirements. No real hardware is needed in simulation mode.

Linux

Qt GUI library dependencies

sudo apt install libxcb-cursor0 libxcb-xinerama0 libegl1-mesa

Camera permissions

Andor / BlackFly cameras require udev rules.

Windows Subsystem for Linux (WSL)

WSL users should make sure to run from a Linux filesystem mount; operating on the Windows filesystem causes unexpected errors.

Web remote control platform (quatm.web)

Upgrading from an older version leaves the quatm.web subpackage missing

When upgrading from an older version, the Web Hub reports an error at startup saying the subpackage is missing: simply re-run pip install -e . (quatm.web is installed together with the quatm core).

Operating the same process from the desktop GUI at the same time

The desktop process manager and the web process page manage the same set of services/GUIs. Before starting an entry, either entry point first terminates the instance managed by the other entry point, so that nothing is launched twice; if a process is restarted unexpectedly, check whether a start action was performed on the other side.

2.4 - Frequently asked questions

Installation, devices, data and upgrade questions.

The questions below are the ones we hear most often — from a first installation to the quiet details that matter once an experiment is running.

Installation and platforms

Which operating systems are supported? Quatm uses Qt 6/PySide6; supported platforms follow the Qt 6.11 matrix: Windows 10 (1809+) and Windows 11 on x86-64, macOS 13 or newer, and Linux x86-64 (Ubuntu 22.04/24.04, Debian 12, or equivalent). ARM64 desktop support is limited; see the Qt 6.11 supported-platform matrix. Python 3.9 or newer is required.

What are the minimum requirements? A machine with at least 8 GB of memory and 4 CPU cores is recommended for the full GUI and data services. Real-time control requires an FPGA-class controller.

Devices and drivers

Do I need specific hardware? No single vendor is required, but a real-time controller with TTL/DAC outputs is needed for timed sequences. Instruments using standard protocols can be integrated by selecting or writing a driver; the effort depends on the protocol and vendor SDK.

How do I integrate a custom device? Write a driver class and register it in device_db.py as type: local (or map channels with attr / generic_attr). See Device drivers.

Which cameras are supported? Andor, FLIR/BlackFly and other scientific cameras are covered by the driver catalogue; custom cameras can be added through project-local drivers.

Data

What format is used? Experiment data is archived as HDF5, with optional streaming/export services.

How large are the files? A typical absorption-imaging run is on the order of 10–50 MB, depending on image count and resolution. Plan disk capacity and retention for long campaigns.

Updating

How do I upgrade? Update the Python environment and reinstall the package, or pull the repository and rerun the bootstrap. Check the changelog for incompatible changes.

Will my experiment scripts break? Quatm follows semantic versioning for public interfaces; major changes are documented with migration notes. Verify in simulation before a campaign.

3 - Experiment editing

The Experiment lifecycle, parameters, timing and scans.

An experiment is a Python class derived from Experiment. The framework calls a small number of well-defined hooks, and provides the parameter and device model around them.

Lifecycle

StageWhat happens
build()Declare parameters and devices; called once per instance
run()Execute one repetition of the experiment
_start_run() / _end_run()Framework bookkeeping around each run, including data markers
close()Release devices and clients

Do not perform hardware work in build(): it may run while the user is editing parameters. Put all timed operations in run().

Parameters

Parameters are declared with typed values such as NumberValue, BoolValue, StringCombo, IntegerValue and ListValue. Each value carries metadata used by the GUI and by the scan engine:

  • unit and display multiplier;
  • minimum/maximum and step;
  • whether the value is persisted;
  • the list of allowed values for combos.

The same metadata drives the desktop parameter panel, the web dashboard and scan validation, so a scan cannot silently use an invalid value.

Timing

delay(seconds) advances the sequence clock. realtime() and the DAC/TTL attribute descriptors schedule outputs on the real-time controller. The experiment code describes what happens; the selected backend decides how to realise it on FPGA, ADwin or a simulated controller.

Keep hardware interactions inside run() and avoid Python-level busy loops. The runtime is designed so that a long analysis or GUI operation cannot block the timing path.

Scans and repetitions

A scan is a list of parameter values, optionally multi-dimensional, with an execution order (including randomised and snake orders). The queue turns a scan into individual task descriptions, each with the same parameter schema. Results are stored with the scan definition so the data remains self-describing.

Errors and cleanup

An exception in run() aborts the current run, executes framework cleanup, and marks the task failed. close() is called even after failures. Device leases and property clients are released deterministically; long-running experiments should rely on this cleanup rather than on process exit.

4 - Device drivers

The device model, driver catalogue and custom driver development.

Quatm separates experiment logic from instrument communication. Every instrument is addressed through a logical name in device_db.py; the driver decides whether that name maps to a serial port, a VISA resource, a TCP socket, a USB device or a vendor DLL.

Access modes

ModeBehaviourWhen to use
localConstructs a driver objectcameras, FPGA controllers, translation stages
attrMaps a channel to a Python attributeDAC/TTL outputs
generic_attrCalls a named driver functionDDS tones, multi-channel settings

All modes support simulation. In simulation the same experiment code runs against a dummy backend, which is essential for developing sequences without occupying the apparatus.

Driver categories

The bundled catalogue covers:

  • Real-time control — ADwin, FPGA, timing and trigger distribution.
  • Lasers and light — Toptica, Moglabs, Vescent, laser locks and shutters.
  • Signal generation and RF — Spectrum AWGs, Rigol, Valon, DDS boards.
  • Scientific cameras — Andor, FLIR/BlackFly, PCO and generic video sources.
  • Motion and positioning — PI stages, Thorlabs, piezo mirrors.
  • DAQ and power supplies — NI, MCC, Keysight, Keithley.
  • Environmental monitoring — temperature controllers, magnetometers, wavemeters, vacuum gauges.

The catalogue and per-driver verification status are tracked in the repository (docs/DRIVER_VERIFICATION.md). A driver is considered verified only after it has been exercised on real hardware.

Writing a driver

A driver is a normal Python class with:

  • __init__ accepting the arguments declared in device_db.py;
  • simulating handling for offline use;
  • explicit close() releasing sockets, serial ports and vendor handles;
  • optional check() / self-test hooks used by the Device Manager.

Place project-specific drivers under <project>/drivers/. They are imported before bundled drivers with the same suffix, so a laboratory can pin a local version without forking the framework.

Device health and leases

Opening a device acquires a cross-process lease, preventing two experiments from silently driving the same instrument. The Device Manager can probe instruments, verify drivers and report the current owner. Bus-level locking protects adapters that cannot be opened concurrently.

4.1 - Scientific cameras

Scientific camera and image acquisition drivers for 15 brands including Andor, Basler, Hamamatsu and FLIR.

Overview

Quatm provides a unified ICamera interface wrapper for 15 major scientific camera brands worldwide, covering EMCCD, sCMOS, high-speed CMOS and industrial cameras.


Andor series: iXon EMCCD / Zyla sCMOS / Shamrock spectrograph

Overview

Quatm provides two Andor camera drivers that deliver high-performance imaging control through the native SDKs:

Driver classSDKApplicable modelsInterface
AndorSDK2Cameraatmcd32d.dll (SDK2)iXon EMCCD, Luca, ClaraPCIe / USB
AndorSDK3Cameraatcore.dll (SDK3)Zyla, Neo, Marana (sCMOS)USB3 / CameraLink
ShamrockSpectrographShamrockCIF.dllSR-303i, SR-500i and other spectrographsUSB

All classes inherit from the io.camera interface and support ROI, binning, exposure control and frame buffer management.

Andor SDK2 (EMCCD) AndorSDK2Camera

iXon EMCCD Andor SDK2 series
CommunicationDLL (atm32cd32)
Readout modeEM/conventional
ADC frequency1/5/10 MHz
Pixel depth16-bit
CoolingTEC (≤ -100°C)
Suited to extremely low light levels (single atom/ion fluorescence), achieving single-photon sensitivity through electron multiplication (EM Gain).
Driver module: AndorSDK2Camera

Initialization

from quatm.drivers.andor.AndorSDK2 import AndorSDK2Camera

# connect the first camera
cam = AndorSDK2Camera(cam_id=0)

# acquisition parameters
cam.setup_acquisition(
    exposure=0.1,       # exposure time (s)
    em_gain=200,        # EM gain (0-1000)
    readout_rate="5MHz",# readout rate
    pre_amp_gain=2,     # pre-amplifier gain
    hsspeed=0,          # horizontal transfer rate
    temperature=-70,    # target temperature (°C)
)

# acquire a single frame
cam.start_acquisition(nframes=1)
image = cam.read_multiple_images(nframes=1)

Key parameters

ParameterDescriptionTypical range
exposureExposure time per frame0.001 – 3600 s
em_gainElectron multiplication gain0 – 1000 (linear or real gain mode)
readout_rateADC readout frequency1MHz / 5MHz / 10MHz
temperatureTarget sensor temperature-60 to -100°C (depends on cooler)
pre_amp_gainPreamplifier gain1× – 4×

Andor SDK3 (sCMOS) AndorSDK3Camera

Zyla / Neo sCMOS Andor SDK3 series
CommunicationDLL (atcore)
SensorsCMOS
InterfaceUSB3 / CameraLink
Pixel depth12/16-bit
Frame rateup to 100 fps (full resolution)
Suited to fast sequence imaging and applications requiring a large field of view. Exposure, ROI and triggering are configured through the GenAPI property model.
Driver module: AndorSDK3Camera

Initialization

from quatm.drivers.andor.AndorSDK3 import AndorSDK3Camera

cam = AndorSDK3Camera(cam_id=0)
cam.setup_acquisition(
    exposure=0.05,
    pixel_format="Mono16",
    trigger_mode="Internal",
)

Andor Shamrock spectrograph ShamrockSpectrograph

Controls grating selection, centre wavelength and slit width:

from quatm.drivers.andor.Shamrock import ShamrockSpectrograph

spec = ShamrockSpectrograph(idx=0)
spec.set_wavelength(780.24)      # centre wavelength (nm)
spec.set_grating(2)              # select grating
spec.set_slit_width(50, port=1)  # entrance slit (µm)

Device configuration

"emccd": {
    "type": "local",
    "module": "drivers.andor.AndorSDK2",
    "class": "AndorSDK2Camera",
    "arguments": {"cam_id": 0},
}

FLIR / Teledyne / Blackfly S / Grasshopper3

Overview

Quatm wraps the Spinnaker SDK in the TeledyneSpinnakerCamera class, supporting all GenICam/GenAPI-compatible FLIR/Teledyne USB3/GigE cameras.

Blackfly S / Grasshopper3 FLIR Spinnaker series
CommunicationSpinnaker SDK
InterfaceUSB3 / GigE
Pixel formatsMono8/16, Bayer
Triggerhardware/software
Frame ratedepends on model
For general laboratory imaging: MOT monitoring, alignment beam imaging and equipment status monitoring.
Driver module: TeledyneSpinnakerCamera

Initialization

from quatm.drivers.teledyne.spinnaker import TeledyneSpinnakerCamera

# list all available cameras
cam_list = TeledyneSpinnakerCamera.list_cameras()

# connect a camera by serial or index
cam = TeledyneSpinnakerCamera(cam_id="serial_xxx")
# or use an index
cam = TeledyneSpinnakerCamera(cam_id=0)

# configure and start acquisition
cam.setup_acquisition(
    exposure=0.02,          # 20 ms exposure
    pixel_format="Mono8",
    width=1920, height=1200,
    offset_x=0, offset_y=0,
)

Key methods

MethodDescription
list_cameras()Enumerate all Spinnaker cameras in the system
setup_acquisition(**kwargs)Configure ROI, exposure, pixel format, etc.
start_acquisition(nframes)Start acquisition of the specified number of frames
read_multiple_images(nframes)Read the acquired images
get_device_info()Get the camera serial number, model and firmware version

Common property operations

Camera parameters are read and written directly by GenAPI node name:

# Set gain
cam.set_attribute("Gain", 10.0)
# Enable auto exposure
cam.set_attribute("ExposureAuto", "Continuous")
# Get sensor temperature
temp = cam.get_attribute("DeviceTemperature")

Device configuration

"monitor_cam": {
    "type": "local",
    "module": "drivers.teledyne.spinnaker",
    "class": "TeledyneSpinnakerCamera",
    "arguments": {"cam_id": 0},
}
  • stop(): Stop capturing images.

  • quatm.drivers.blackfly.bfly.Camserver(name, serial)

    class quatm.drivers.blackfly.bfly.Camserver(object):
        def __init__(self, name, serial):
            pass
    
        def generateImageIndex(self):
            pass
    
        def run(self):
            pass
    
        def sendimage(self, im):
            pass
    
        def updateBusInfo(self):
            pass
    
    • generateImageIndex(): In the current measurement, count the images. If the next run has started (either task, repetition or run changes), reset the counter (imgindex).

    • run(): Run loop running forever. Sends images from the camera to the imagestream and initiates reconfiguration of the camera in case properties have changed.

    • sendimage(im): Send Blackfly camera image via the imagestream. The image is converted to numpy array, additional information like binning, timestamp, etc., is added to a dictionary and both are sent to the imagestream.

      • Args: im - Blackfly image object.
      • Returns: None
    • updateBusInfo(): Detect cameras on the bus and update info in properties if new camera has been found.

  • quatm.drivers.blackfly.bfly.pc2

    class quatm.drivers.blackfly.bfly.pc2(object):
        pass
    
    • Dummy class in case driver is not installed.

Function:

  • quatm.drivers.blackfly.bfly.run(name)

    def quatm.drivers.blackfly.bfly.run(name):
        pass
    
    • Initialize a Camserver and run it.

Thorlabs TSI / uc480 scientific cameras

TLCamera ThorlabsTLCamera

TSI series Thorlabs scientific camera
CommunicationTLCamera SDK
InterfaceUSB3
SensorsCMOS / CCD
SupportROI, binning
For high-frame-rate scientific imaging with SDK-level control.
Driver module: ThorlabsTLCamera
from quatm.drivers.thorlabs.TLCamera import ThorlabsTLCamera
cam = ThorlabsTLCamera(serial="12345678")
cam.setup_acquisition(exposure=0.01, width=1024, height=768)

Basler — Pylon SDK series

Basler ace / dart Pylon SDK series
CommunicationPylon SDK (GenICam)
InterfaceUSB3 / GigE
SensorCMOS
Pixel formatsMono8/16, Bayer
FeaturesROI, Binning, hardware trigger
Industrial CMOS cameras, suited to routine laboratory imaging tasks such as MOT fluorescence monitoring and alignment beam imaging.
Driver module: BaslerPylonCamera
from quatm.drivers.Basler.pylon import BaslerPylonCamera

# Enumerate all cameras
cams = BaslerPylonCamera.list_cameras()
cam = BaslerPylonCamera(cam_id=cams[0])

cam.setup_acquisition(exposure=0.02)
cam.start_acquisition(nframes=1)

Hamamatsu ORCA series sCMOS

ORCA-Fusion / ORCA-Flash4.0 Hamamatsu DCAM
CommunicationDCAM SDK (DLL)
SensorsCMOS
Pixels2048×2048 (Fusion)
Frame rateup to 100 fps
Readout noise< 1.6 e⁻
Ultra-low-noise sCMOS, suited to quantitative single atom/single ion fluorescence imaging.
Driver module: DCAMCamera
from quatm.drivers.DCAM.DCAM import DCAMCamera

cam = DCAMCamera(idx=0)
cam.setup_acquisition(exposure=0.1, pixel_format="Mono16")

Photometrics Prime / Evolve series

Prime BSI / Evolve EMCCD Photometrics PVCAM
CommunicationPVCAM SDK (DLL)
Sensorback-illuminated sCMOS / EMCCD
InterfacePCIe / USB3
Pixel depth16-bit
Back-illuminated sCMOS offers high quantum efficiency; EMCCD models provide single-photon sensitivity.
Driver module: PVCAMCamera
from quatm.drivers.Photometrics.pvcam import PVCAMCamera

cam = PVCAMCamera(cam_name="Prime_BSI_95B")
cam.setup_acquisition(exposure=0.05)

Princeton Instruments PIXIS / ProEM

PIXIS / ProEM / IsoPlane Princeton PICam
CommunicationPICam SDK (DLL)
SensorCCD / EMCCD
Coolingliquid nitrogen / TEC
InterfacePCIe / USB
Spectroscopy-grade CCD/EMCCD cameras, commonly used together with Acton/IsoPlane spectrographs.
Driver module: PicamCamera
from quatm.drivers.PrincetonInstruments.picam import PicamCamera

cam = PicamCamera(cam_id=0)
cam.setup_acquisition(exposure=1.0)  # long-exposure spectrum acquisition

PCO edge / dimax high-speed cameras

edge / dimax / pco.panda PCO SC2
CommunicationSC2 SDK (DLL)
InterfaceCameraLink HS / USB3 / GigE
Frame rateup to several thousand fps
SensorsCMOS / high-speed CMOS
Suited to fast dynamical processes: MOT loading dynamics and real-time observation of ion crystal reordering.
Driver module: PCOSC2Camera
from quatm.drivers.PCO.SC2 import PCOSC2Camera

cam = PCOSC2Camera(interface="USB 3.0")
cam.setup_acquisition(exposure=0.001)  # 1 ms high-speed acquisition

Allied Vision Alvium / Mako

Alvium / Mako / Bonito Allied Vision Vimba X
CommunicationVimbaX SDK (GenICam)
InterfaceUSB3 / GigE / CameraLink
SensorCMOS
Compact industrial cameras, suited to space-constrained optical path integration.
Driver module: AlliedVisionVimbaXCamera
from quatm.drivers.AlliedVision.VimbaX import AlliedVisionVimbaXCamera

cam = AlliedVisionVimbaXCamera(cam_id=0)

IDS uEye+ / Ensenso

uEye+ / Ensenso 3D IDS peak
CommunicationIDS peak SDK
InterfaceUSB3 / GigE
SensorCMOS
General-purpose USB/GigE cameras, including the Ensenso 3D stereo camera.
Driver module: GenericIDSPeakCamera
from quatm.drivers.IDS.peak import GenericIDSPeakCamera

cam = GenericIDSPeakCamera(cam_id=0)

Generic configuration pattern

All cameras are registered in device_db.py with type: "local":

"imaging_cam": {
    "type": "local",
    "module": "drivers.Basler.pylon",
    "class": "BaslerPylonCamera",
    "arguments": {"cam_id": 0},
}

4.2 - Signal sources and RF

Drivers for frequency synthesizers and RF instruments: Valon 5009, DDS AD9959, Agilent, Rigol.

Overview

Quatm supports a broad range of RF instruments, from general-purpose function generators to vector network analyzers, covering frequency synthesis, arbitrary waveforms and S-parameter measurement.


Valon 5009 dual-channel microwave frequency synthesizer

Overview

The Valon 5009 is a dual-channel frequency synthesizer (23.5 MHz – 6 GHz) controlled over a serial port. Quatm wraps it in valon, which sets the frequency and power of the two outputs independently.

Valon 5009 Dual RF Synthesizer
Communicationserial (RS232)
Frequency range23.5 MHz – 6 GHz
Channels2
Output power-30 ~ +10 dBm
Frequency resolution0.1 Hz
Widely used in ion trap experiments to drive microwave transitions (such as the Ba⁺ 9.9 GHz hyperfine transition) or as an AOM drive source.
Driver module: valon

Initialization

from quatm.drivers.valon.valon_quatm import valon

# Connect to Valon (default /dev/ttyUSB0)
valon = valon(port="/dev/ttyUSB0")

# Set channel 1 frequency to 500 MHz
valon.set_frequency(freq=500e6, source=1)

# Set channel 2 frequency to 9.925 GHz (external frequency multiplier required)
valon.set_frequency(freq=9.925e9, source=2)

# Send raw command
valon.sendRaw("LOCK 1 ON\r\n")

Key methods

MethodDescription
set_frequency(freq, source)Set the output frequency of channel 1 or 2 (Hz)
sendCommand(source, command, value)Send a formatted SCPI command
sendRaw(cmd)Send a raw string command

Serial communication

Underneath, the Valon driver communicates through VSerialPort (a serial.Serial subclass). Typical parameters:

# serial parameters (driver defaults)
port = "/dev/ttyUSB0"
baudrate = 115200
timeout = 0.1
Ba⁺ microwave transition scan Ba⁺

Use the Valon 5009 to drive the 9.925 GHz hyperfine transition and measure Rabi oscillations by scanning the frequency.

Key parameters
Start frequency9.924 GHz
Stop frequency9.926 GHz
Step10 kHz
Repetitions per point100
# experiment snippet: frequency scan
import numpy as np
frequencies = np.arange(9.924e9, 9.926e9, 10e3)
for f in frequencies:
    valon.set_frequency(freq=f, source=2)
    # ... run the probe sequence ...

class quatm.drivers.valon.valon_prog.Valon_Standalone(name='Valon')

Bases: object

  • Standalone driver for the Valon frequency generator. Commands can be sent via the Commandhub and are processed in parallel to the experiment, thereby the experiment is not delayed by the Valon.

    • run()
  • function quatm.drivers.valon.valon_prog.run()


DDS AD9959 four-channel direct digital synthesis

Overview

The AD9959 driver wraps the AD9959 evaluation board and controls four independent DDS channels over the SPI bus, providing precise frequency, amplitude and phase control.

AD9959 evaluation board 4-channel DDS
CommunicationSPI
Channels4
Frequency resolution32-bit (0.12 Hz @ 500 MHz clock)
Phase resolution14-bit
Amplitude control10-bit DAC
Widely used in cold atom/ion experiments for AOM driving, sideband generation and coherent manipulation.
Driver module: AD9959

Initialization

from quatm.drivers.dds import AD9959

dds = AD9959()

# Configure channel 0 — cooling-light AOM
dds.set_frequency(0, 80e6)       # 80 MHz
dds.set_amplitude(0, 0.5)        # 50% amplitude
dds.set_phase(0, 0)              # 0° phase

# configure channel 1 — repump AOM (+0.1 MHz offset)
dds.set_frequency(1, 80.1e6)
dds.set_amplitude(1, 0.3)

Key methods

MethodDescription
set_frequency(ch, freq_hz)Set the channel frequency (Hz)
set_amplitude(ch, amp)Set the channel amplitude (0–1 fraction)
set_phase(ch, phase_deg)Set the channel phase (°)
update_all()Update all channels at once

Device configuration

"aom_dds": {
    "type": "local",
    "module": "drivers.DDS",
    "class": "AD9959",
}

Typical applications

ApplicationConfiguration
AOM driveSingle channel, fixed frequency
Raman transitionsTwo channels, difference-frequency locking
Trap driveMulti-channel phase control
Sideband coolingFrequency modulation

Alternative driver

An alternative AD9959 board driver with a serial (AT command) interface is also available:

from quatm.drivers.dds.alibaba import AD9959Board

Rigol DG1020Z two-channel function generator

DG1020Z

DG1020Z Rigol function generator
CommunicationVISA (USB/LAN)
Channels2
Frequency range1 µHz – 25 MHz
Amplitude1 mVpp – 10 Vpp
Sampling rate200 MSa/s
Used to generate AOM/EOM drive signals, modulation waveforms and custom arbitrary waveforms.
Driver module: RigolDG1020Z
from quatm.drivers.AWG.specific import RigolDG1020Z
fg = RigolDG1020Z(addr="USB0::...")
fg.set_frequency(80e6)
fg.set_amplitude(1.0)       # 1.0 Vpp
fg.enable_output(channel=1, enable=True)
fg.set_function(channel=1, function="SIN")

Other Rigol devices

DP1116A programmable DC power supply

DP1116A Rigol DC power supply
CommunicationVISA
Output16V/10A or 32V/5A
Channels1
Resolution1 mV / 1 mA
Used for coil current control, laser diode driving and circuit board supply.
Driver module: DP1116A
from quatm.drivers.rigol.power_supply import DP1116A

ps = DP1116A(addr="USB0::0x1AB1::0x0E11::DP1A123456789::INSTR")
ps.set_voltage(12.0)       # 12V
dc.set_current(0.5)        # 500 mA current limit
ps.enable_output(True)

Oscilloscopes

Rigol DS series oscilloscopes are compatible with the Tektronix SCPI command set and are controlled through the ITektronixScope driver:

from quatm.drivers.tektronix.base import ITektronixScope

scope = ITektronixScope(addr="USB0::0x1AB1::0x04CE::DS123456789::INSTR", nchannels=4)
scope.set_timebase(scale=1e-6)     # 1 µs/div
scope.set_vertical(channel=1, scale=0.5)  # 0.5 V/div
waveform = scope.acquire(channel=1)

Agilent / Keysight 33500 series arbitrary waveform generators

33500B / 33220A / 33250A Agilent/Keysight
CommunicationVISA (USB/LAN/GPIB)
Channels1–2
Frequencyup to 120 MHz (33500B)
Resolution14-bit
Sampling rate250 MSa/s
Waveformssine/square/ramp/pulse/noise/ARB
A laboratory standard signal source for AOM driving, modulation signals and reference clock generation.
Driver module: Agilent33500 / Agilent33220A
from quatm.drivers.awg.specific import Agilent33500

awg = Agilent33500(addr="USB0::0x0957::0x2C07::MY52801234::INSTR")

# Configure an 80 MHz sine wave
awg.set_function(channel=1, function="SIN")
awg.set_frequency(80e6)
awg.set_amplitude(1.0)      # 1 Vpp
awg.enable_output(channel=1, enable=True)

# Burst mode — generate N cycles, then stop automatically
awg.enable_burst(channel=1, ncycles=100)

Supported models

Driver classApplicable models
Agilent3350033500B, 33510B, 33520B, 33522B
Agilent33220A33220A, 33250A
RigolDG1000DG1022, DG1032
RigolDG1020ZDG1022Z, DG1032Z

Instek AFG2000 / AFG2225 dual channel

AFG-2225 / AFG-2100 Instek
CommunicationVISA
Channels1 (AFG2000) / 2 (AFG2225)
Frequencyup to 25 MHz
Sampling rate200 MSa/s
A cost-effective two-channel arbitrary waveform generator.
Driver module: InstekAFG2225 / InstekAFG2000
from quatm.drivers.awg.specific import InstekAFG2225

awg = InstekAFG2225(addr="USB0::...")
awg.set_frequency(10e6)
awg.set_amplitude(2.0)
awg.enable_output(channel=1, enable=True)
awg.enable_output(channel=2, enable=True)

Tektronix AFG1000 arbitrary waveform generator

AFG1022 / AFG1062 Tektronix
CommunicationVISA
Channels2
Frequencyup to 60 MHz (AFG1062)
Sampling rate300 MSa/s
Arbitrary waveform64 MB memory
A high-performance two-channel AWG with support for large custom waveforms.
Driver module: TektronixAFG1000
from quatm.drivers.awg.specific import TektronixAFG1000

awg = TektronixAFG1000(addr="USB0::...")
awg.set_function(channel=1, function="ARB")
# Load a custom waveform...

Tektronix DPO/MSO series oscilloscopes

DPO2000 / MSO4000 series Tektronix oscilloscope
CommunicationVISA
Bandwidth100 MHz – 1 GHz
Channels2–4
Sampling rateup to 5 GS/s
Automates waveform acquisition and parameter measurement over SCPI. Frequently used for experiment debugging and pulse sequence verification.
Driver module: ITektronixScope
from quatm.drivers.tektronix.base import ITektronixScope

scope = ITektronixScope(
    addr="USB0::0x0699::0x0401::C012345::INSTR",
    nchannels=4
)

# Configure trigger
scope.set_trigger(source=1, level=0.5, slope="POS")
scope.set_timebase(scale=1e-6)          # 1 µs/div
scope.set_vertical(channel=1, scale=0.1) # 0.1 V/div

# Acquire waveform
waveform = scope.acquire(channel=1)

Agilent E5071C vector network analyzer

E5071C Agilent VNA
CommunicationVISA
Frequency9 kHz – 20 GHz
Ports2–4
FunctionsS-parameters, impedance, Smith chart
Used for RF component characterization: S-parameter measurement of filters, amplifiers, antennas and transmission lines.
Driver module: AgilentE5071C
from quatm.drivers.Agilent.vna import AgilentE5071C

vna = AgilentE5071C(addr="TCPIP::192.168.1.100::INSTR")
vna.set_frequency_range(start=1e6, stop=1e9, points=1001)
vna.set_if_bandwidth(100)       # 100 Hz IFBW
vna.set_averaging(count=10)

# Read S21 parameters
freq, s21 = vna.read_S_parameters(parameter="S21")

PicoVNA portable vector network analyzer

PicoVNA Pico Technology
CommunicationTCP/SCPI (port 5025)
Frequency300 kHz – 8.5 GHz
Ports2
Dynamic range

100 dB

A compact USB-controlled VNA, well suited to desktop RF debugging in the laboratory.
Driver module: PicoVNA
from quatm.drivers.PicoTech.vna_scpi import PicoVNA

vna = PicoVNA(addr="localhost")  # or IP address
vna.set_frequency_range(start=10e6, stop=500e6)
s_params = vna.read_S_parameters(parameter="S21")

Generic SCPI device configuration

"function_gen": {
    "type": "local",
    "module": "drivers.awg.specific",
    "class": "Agilent33500",
    "arguments": {"addr": "USB0::..."},
}

4.3 - Lasers and optics

Drivers for lasers and optical measurement: Cobolt, Toptica, Sirah Matisse, M² Solstis, HighFinesse wavemeters.

Overview

Quatm supports diode lasers, pump lasers, tunable ring lasers and optical measurement instruments. Laser-type devices are controlled over serial ports; SCPI instruments inherit the standard interface.


Hubner Cobolt MLD / DPL single-frequency lasers

Cobolt 06-01 series Hubner Cobolt
Communicationserial (115200 baud)
Wavelength405/445/488/515/532/561/594/633/660/780/1064 nm
Power25–300 mW
Modulationanalog + digital TTL (up to 5 MHz)
Compact DPSS/diode lasers for ion/atom cooling light, repump light and fluorescence excitation.
Driver module: Cobolt
from quatm.drivers.Hubner.Cobolt import Cobolt

laser = Cobolt(conn="/dev/ttyUSB0")

# Turn on the laser
laser.set_autostart(True)
laser.set_power(50e-3)         # 50 mW

# Digital modulation (TTL)
laser.set_digital_modulation(True)
laser.set_modulation_power(100e-3)  # High power during modulation

# Read operating parameters
hours = laser.get_operating_hours()
temp = laser.get_base_temperature()

Toptica iBeam smart diode lasers

iBeam series Toptica
Communicationserial (115200 baud)
Wavelength375–1550 nm
Smart laser heads with a built-in controller, supporting automatic current/power calibration and status diagnostics.
Driver module: TopticaIBeam
from quatm.drivers.Toptica.ibeam import TopticaIBeam

laser = TopticaIBeam(conn="/dev/ttyUSB0")
laser.set_power(channel=0, power=10e-3)
laser.enable(channel=0, enable=True)

Laser Quantum Finesse pump laser

Finesse Laser Quantum
Communicationserial (19200 baud)
Power4–16 W (532 nm)
Noise< 0.1% RMS
ApplicationTi:sapphire/dye laser pumping
An ultra-low-noise continuous-wave DPSS laser used to pump tunable ring lasers such as the Sirah Matisse.
Driver module: Finesse
from quatm.drivers.LaserQuantum.base import Finesse

pump = Finesse(conn="/dev/ttyUSB0")
pump.set_power(10.0)           # 10 W
pump.enable(True)
actual_power = pump.get_power()

Lighthouse Photonics Sprout G

Sprout G Lighthouse Photonics
Communicationserial (19200 baud)
Wavelength532 nm
Power6–18 W
Noise< 0.2% RMS
A high-power 532 nm solid-state pump laser for Ti:sapphire lasers and optical dipole traps.
Driver module: SproutG
from quatm.drivers.LighthousePhotonics.base import SproutG

sprout = SproutG(conn="/dev/ttyUSB0")
sprout.set_power(15.0)
sprout.enable(True)

Sirah Matisse tunable ring laser

Matisse Sirah Lasertechnik
CommunicationVISA or TCP/IP
Wavelength rangevisible–near-infrared (depends on dye/Ti:sapphire)
Linewidth< 50 kHz (locked)
Tuning elementsbirefringent filter + thin/thick etalon + piezo
A precision tunable ring laser system. MatisseTuner coordinates the laser with a HighFinesse wavemeter to provide automatic wavelength tuning and stitched scans.
Driver module: SirahMatisse / MatisseTuner
from quatm.drivers.Sirah.Matisse import SirahMatisse

matisse = SirahMatisse(addr="TCPIP::192.168.1.10::INSTR")

# Set wavelength
matisse.set_birefringent_filter(wavelength=780.24)  # nm
matisse.set_thin_etalon(wavelength=780.24)
matisse.set_thick_etalon(wavelength=780.24)

# Piezo scan
matisse.set_piezo_voltage(50.0)   # 0–100 V
# Automatic tuning (requires a wavemeter)
from quatm.drivers.Sirah.tuner import MatisseTuner

tuner = MatisseTuner(laser=matisse, wavemeter=wlm)
tuner.tune_to(wavelength=780.24)  # Auto-search and lock
tuner.scan(start=780.20, stop=780.30, speed=0.01)  # nm/s

M² Solstis narrow-linewidth tunable laser

Solstis M² Lasers
CommunicationTCP/IP JSON + WebSocket
Wavelength range700–1000 nm (Ti:sapphire)
Linewidth< 50 kHz
Functionsetalon lock, cavity reference lock, wavemeter feedback
A fully digital tunable laser system, completely remotely controllable through a TCP/IP JSON interface. It supports a WebSocket real-time status stream.
Driver module: Solstis (ICE Bloc)
from quatm.drivers.M2.solstis import Solstis

solstis = Solstis(addr="192.168.1.20", port=8080)

# Wavelength tuning
solstis.set_wavelength(780.24)
# Etalon lock
solstis.lock_etalon()
# Terahertz scan (with the EMM module)
solstis.set_terahertz_scan(start_freq=0, stop_freq=100e9)

Optical power meters & wavemeters

Ophir Vega laser power/energy meter

Vega Ophir
Communicationserial
Headsthermopile/photodiode/pyroelectric
PowerµW – kW
EnergyµJ – J
A versatile laser measurement instrument supporting continuous-wave power and pulsed energy measurement.
Driver module: VegaPowerMeter
from quatm.drivers.Ophir.base import VegaPowerMeter

pm = VegaPowerMeter(conn="/dev/ttyUSB0")
power = pm.get_power()

HighFinesse WS6/WS7 precision wavemeters

WS6 / WS7 HighFinesse
Communicationnative wlmData.dll
Accuracy2–200 MHz (depends on model)
Channels1–8
Measurement rateup to 500 Hz
Wavelength range350–1120 nm
Fizeau interferometer wavemeters for precision laser frequency measurement and feedback control. Multi-channel models can monitor the frequencies of several lasers simultaneously.
Driver module: WLM
from quatm.drivers.HighFinesse.wlm import WLM

wlm = WLM(version=7, dll_path="C:\\wlm\\wlmData.dll")

# Read the current frequency of channel 1
freq = wlm.get_frequency(channel=1)    # THz
wavelength = wlm.get_wavelength(channel=1)  # nm

# Exposure control (auto-adapts to light intensity)
wlm.set_exposure(channel=1, mode="auto")

Thorlabs optical power meters PM100 / PM400

PM100 / PM400 series Thorlabs power meter
CommunicationVISA (USB)
Supported headsphotodiode/thermopile/pyroelectric
Wavelength rangedepends on head
Power rangenW – W level
Communicates with Thorlabs power meter heads over SCPI commands and supports a variety of sensor head types.
Driver module: GenericPM
from quatm.drivers.Thorlabs.misc import GenericPM
pm = GenericPM(addr="USB0::0x1313::0x8078::P1234567::INSTR")
pm.set_wavelength(780.0)    # 780 nm calibration
power = pm.get_power()      # Current power (W)

4.4 - Motion and positioning

Drivers for precision motion and positioning: PI piezo nanopositioners, Attocube, Newport Picomotor, Thorlabs APT.

Overview

Quatm supports a wide range of motion platforms, from piezo nanopositioning to long-travel stepper motors, behind a unified IMultiaxisStage interface.


Physik Instrumente (PI) piezo nanopositioning

Overview

GenericPIController wraps serial communication with PI general-purpose controllers and controls a variety of PI piezo positioning platforms through the ASCII command set.

PI piezo controller E-xxx series
Communicationserial (RS232)
ProtocolPI ASCII command set
Axesdepends on controller model
Accuracysub-nanometre
Typical applications include fine focusing of high-NA objectives, automatic fibre coupling alignment, interferometer beam path adjustment and control of the beam waist position of optical dipole traps.
Driver module: GenericPIController

Initialization

from quatm.drivers.PhysikInstrumente.base import GenericPIController

# serial connection
stage = GenericPIController(conn="/dev/ttyUSB0")

# home
stage.home(axis=0)

# absolute positioning (µm)
stage.move_to(axis=0, position=50.0)

# Relative move
stage.move_by(axis=1, delta=10.0)

# query current position
pos = stage.get_position(axis=0)

Key methods

MethodDescription
home(axis)Home the specified axis
move_to(axis, position)Absolute positioning (µm)
move_by(axis, delta)Relative move (µm)
get_position(axis)Query the current position
stop(axis)Emergency stop

Device configuration

"objective_z": {
    "type": "local",
    "module": "drivers.PhysikInstrumente.base",
    "class": "GenericPIController",
    "arguments": {"conn": "/dev/ttyUSB0"},
}

Usage example

# In the Experiment class
self.pi_stage = self.get_device("pi_stage")

# Move to the target position (µm)
self.pi_stage.move_absolute(x=50.0, y=25.0, z=10.0)

# Relative move
self.pi_stage.move_relative(dx=1.0, dy=0.0, dz=0.0)

Note: piezo translation stages have a limited travel range, and exceeding it triggers hardware protection. Consult the device manual to confirm the maximum travel before use.


Thorlabs APT/Kinesis translation stages

KDC101 / K10CR1 series

KDC101 / K10CR1 / filter wheel Thorlabs APT/Kinesis
Communicationserial (FTDI)
ProtocolAPT (binary)
Applicable devicesDC servo motors, stepper motors, filter wheels, shutters
A unified wrapper for Thorlabs APT protocol devices.
Driver module: BasicKinesisDevice
from quatm.drivers.Thorlabs.kinesis import BasicKinesisDevice
stage = BasicKinesisDevice(conn="27200001")
stage.home()
stage.move_to(position=25.0)    # mm

Elliptec piezoelectric resonant motors

ELLx series Thorlabs Elliptec
Communicationserial (9600 baud)
Traveldepends on slider
Accuracyµm level
Compact piezoelectric resonant motors, suited to space-constrained optical mounts.
Driver module: ElliptecMotor
from quatm.drivers.Thorlabs.elliptec import ElliptecMotor
motor = ElliptecMotor(conn="/dev/ttyUSB0", addrs=[0])
motor.move_to(axis=0, position=10.0)

Motorized filter wheels FW102/FW212

from quatm.drivers.Thorlabs.serial import FW
fw = FW(conn="/dev/ttyUSB0")
fw.set_position(position=2)    # switch to filter position 2

KDC101 motorized translation stage

Control Thorlabs KDC101/T-Cube series motorized translation stages through the .NET SDK.

from quatm.drivers.thorlabs.kdc101 import KDC101

stage = KDC101("26001234")   # Serial number
stage.home()                  # Home
stage.move_absolute(10.0)     # Move to 10.0 mm
pos = stage.get_position()    # Read current position
FunctionDescription
HomingAutomatically seek the limit switch
Absolute moveMove to the specified position (mm)
Relative moveMove relative to the current position
Position readoutRead the current position in real time

Attocube ANC300 / ANC350 nanopositioning

ANC300 Attocube
CommunicationTCP/IP (port 7230)
Axesup to 7
Travelmm range
Accuracynm range
Suitable forcryogenic / vacuum
A modular nanopositioning controller, remotely controlled over TCP/IP. It is widely used for precise positioning of objectives and samples inside cryostats.
Driver module: ANC300
from quatm.drivers.Attocube.anc300 import ANC300

anc = ANC300(conn=("192.168.1.30", 7230))

# home
anc.home(axis=0)

# Nanometer-level step
anc.move_by(axis=0, delta=0.1)  # 100 nm

# Continuous scan mode
anc.set_continuous_mode(axis=1, enable=True)
anc.set_continuous_speed(axis=1, speed=100.0)  # nm/s
ANC350 Attocube (USB)
CommunicationUSB HID (PyUSB)
Axesup to 3
Controldirect USB connection
USB variant, suited to desktop applications.
Driver module: ANC350
from quatm.drivers.Attocube.anc350 import ANC350

anc = ANC350(conn=0)  # First USB device
anc.move_to(axis=0, position=50.0)

Newport Picomotor 8742 4-axis controller

Picomotor 8742 Newport
CommunicationUSB (PyUSB) or TCP/IP Telnet (port 23)
Axes4
Step size< 30 nm
Vacuum compatibleoptional
A piezoelectric inertial motor controller supporting direct USB or network control. Vacuum-compatible models can be used directly for adjusting optical elements inside a vacuum chamber.
Driver module: Picomotor8742
from quatm.drivers.Newport.picomotor import Picomotor8742

# USB connection
pico = Picomotor8742(conn=0)

# Or TCP/IP
pico = Picomotor8742(conn="192.168.1.40", multiaddr=(0, 1))

pico.move_to(axis=0, position=5.0)  # mm
pico.home(axis=0)

SmarAct MCS2 / SCU3D high-precision positioning

MCS2 SmarAct
Communicationnative MCS2 SDK
Axesup to 18
Accuracynm range
Speedup to 20 mm/s
Vacuum/cryogenicoptional modules
A modular high-precision positioning system supporting coordinated multi-axis motion and complex trajectories.
Driver module: MCS2
from quatm.drivers.SmarAct.MCS2 import MCS2

mcs = MCS2(locator="usb:sn:12345678")
mcs.move_to(axis=0, position=10.0)  # mm
mcs.get_position(axis=0)
SCU3D SmarAct 3-axis
CommunicationSCU3DControl SDK
Axes3
An integrated 3-axis controller for confined spaces.
Driver module: SCU3D
from quatm.drivers.SmarAct.scu3d import SCU3D

scu = SCU3D(idx=0)
scu.move_to(axis=1, position=25.0)

Standa 8SMC stepper motor controllers

8SMC4 / 8SMC5 Standa
Communicationserial (115200 baud)
Axes1–4
Step resolutiondepends on motor/lead screw
Applicationlong-travel translation stages, rotation stages
An economical stepper motor controller, suited to long-travel coarse positioning on optical tables.
Driver module: Standa8SMC
from quatm.drivers.standa.base import Standa8SMC

stage = Standa8SMC(conn="/dev/ttyUSB0")
stage.move_to(position=50.0)  # mm
stage.home()

Arcus Performax multi-axis stepper control

Performax Arcus Technology
CommunicationUSB (DLL) or RS485 serial
Axesup to 4
Featuresacceleration/deceleration profiles, limit detection
A high-performance stepper motor controller providing smooth S-curve acceleration and deceleration.
Driver module: GenericPerformaxStage
from quatm.drivers.arcus.performax import GenericPerformaxStage

stage = GenericPerformaxStage(idx=0, conn="USB")
stage.move_to(axis=0, position=100.0)
stage.set_acceleration(axis=0, accel=50.0)   # mm/s²

Trinamic TMCM-1110 integrated stepper motor

TMCM-1110 Trinamic
Communicationserial (9600 baud, TMCL protocol)
FeaturesStallGuard stall detection, CoolStep energy saving
An integrated stepper motor module with a built-in controller and driver. Suited to simple rotation/translation tasks and beam path shutters.
Driver module: TMCM1110
from quatm.drivers.trinamic.base import TMCM1110

motor = TMCM1110(conn="/dev/ttyUSB0")
motor.move_to(position=180.0)  # Rotation (°)
motor.set_velocity(500.0)      # °/s

Generic motion platform configuration

"objective_z": {
    "type": "local",
    "module": "drivers.Attocube.anc300",
    "class": "ANC300",
    "arguments": {"conn": ("192.168.1.30", 7230)},
}

4.5 - DAQ and power supplies

Data acquisition and electrical measurement drivers: NI DAQ, Rigol/EA power supplies, Keithley/Voltcraft multimeters, oscilloscopes.

Overview

Data acquisition and power equipment form the infrastructure of experiment automation. Quatm provides a unified NI-DAQmx wrapper together with SCPI/Modbus instrument drivers.


Rigol DP1116A programmable DC power supply

DP1116A Rigol DC power supply
CommunicationVISA
Output16V/10A or 32V/5A
Channels1
Resolution1 mV / 1 mA
Used for coil current control, laser diode driving and circuit board supply.
Driver module: DP1116A
from quatm.drivers.rigol.power_supply import DP1116A

ps = DP1116A(addr="USB0::0x1AB1::0x0E11::DP1A123456789::INSTR")
ps.set_voltage(12.0)       # 12V
dc.set_current(0.5)        # 500 mA current limit
ps.enable_output(True)

Rigol DP932 programmable DC power supply

Provides programmable DC power for a wide range of laboratory equipment.

from quatm.drivers.rigol.dp932 import RigolDP932

psu = RigolDP932("USB0::...")
psu.set_voltage(1, 5.0)     # Channel 1: 5.0 V
psu.set_current(1, 1.0)     # Channel 1: 1.0 A current limit
psu.output_on(1)

NI-SWITCH matrix/multiplexer switches

PXI-25xx / SCXI-11xx series NI-SWITCH
CommunicationNI-SWITCH API
Topologymatrix/multiplexer/general-purpose
Channelsdepends on module
Switching speedms range
Maximum voltage300 V (depends on module)
NI switch modules are controlled through the niswitch Python API. They are suited to signal routing, multiplexed calibration and coil polarity reversal in experiments.
Driver module: ni_switch
from quatm.drivers.NI.ni_switch import NISwitch

sw = NISwitch(resource_name="PXI1Slot2")
sw.connect(channel1="ch0", channel2="com0")
sw.disconnect(channel1="ch0", channel2="com0")
all_relays = sw.get_relay_count()

NI-Digital digital waveform generator/analyzer

PXIe-657x / PXIe-655x series NI-Digital
CommunicationNI-Digital Pattern API
Channels32–128
Rateup to 200 MHz
Voltage1.2–5.5 V (programmable)
Featurespattern generation/acquisition, PMU parametric measurement
NI digital waveform instruments are controlled through the nidigital Python API. They generate and acquire high-speed digital waveforms for custom TTL timing protocols and chip-level digital testing.
Driver module: ni_digital
from quatm.drivers.NI.ni_digital import NIDigital

dig = NIDigital(resource_name="PXI1Slot3")
dig.configure_voltage_levels(voltage=3.3)
pattern = [0b10101010] * 100  # 100-cycle digital waveform
dig.burst_pattern(pattern, frequency=10e6)

NI-DAQmx multifunction data acquisition

PCIe-63xx / USB-63xx / PXI series NI DAQ
CommunicationNI-DAQmx
Analog input16-bit, up to 2 MS/s
Analog output16-bit
Digital I/OTTL, up to 10 MHz
Counters2–4 channels
General-purpose multifunction DAQ cards for analog signal recording, timed digital I/O and photon counting.
Driver module: NIDAQ
from quatm.drivers.NI.daq import NIDAQ

daq = NIDAQ(
    dev_name="Dev1",
    rate=100_000,         # 100 kS/s
    buffer_size=10_000,
)

# Analog input (continuous acquisition)
daq.configure_ai(channels=["ai0", "ai1"], voltage_range=(-10, 10))
daq.start_ai()
data = daq.read_ai(samples=1000)

# Analog output
daq.configure_ao(channels=["ao0"], voltage_range=(0, 5))
daq.write_ao(channel="ao0", voltage=3.3)

# Digital I/O
daq.configure_di(lines=["port0/line0"])
daq.configure_do(lines=["port0/line1"])
daq.write_do(line="port0/line1", value=True)

# Counters (photon counting / frequency measurement)
daq.configure_counter(
    counter="ctr0",
    mode="edge_count",
    source="PFI0",
)
counts = daq.read_counter("ctr0")

Conrad relay board controller

Relay Board Conrad
Communicationserial (19200 baud, binary protocol)
Channels8 relays per board, daisy-chainable
ContactsAC/DC switching
An economical multi-channel relay controller suited to coarse switching: magnetic trap coil polarity reversal, device power switching and laser shutter control.
Driver module: RelayBoard
from quatm.drivers.Conrad.base import RelayBoard

# Single board
rb = RelayBoard(conn="/dev/ttyUSB0", start_addr=0)
rb.set_relay(relay=0, state=True)   # Close relay 1
rb.set_relay(relay=3, state=False)  # Open relay 4

# Byte write (controls 8 channels at once)
rb.write_byte(0b10101010)

Elektro Automatik PS2000B high-power supply

PS2000B series Elektro Automatik
Communicationserial (115200 baud)
Power640 W – 15 kW (depends on model)
Voltageup to 1500 V
Currentup to 510 A
Modesconstant voltage/current/power
High-power programmable DC supply for driving large currents in magnetic trap coils and for Feshbach resonance field coils.
Driver module: PS2000B
from quatm.drivers.ElektroAutomatik.base import PS2000B

ps = PS2000B(conn="/dev/ttyUSB0", remote_mode=True)

# Constant-current mode — magnetic field coil
ps.set_current(50.0)      # 50 A
ps.set_voltage_limit(20.0) # 20 V voltage limit
ps.enable_output(True)

# Read actual values
actual_v = ps.get_voltage()
actual_i = ps.get_current()

Keithley 2110 benchtop digital multimeter

2110 Keithley
CommunicationVISA (USB/LAN)
FunctionsDCV/ACV/DCI/ACI/resistance/capacitance/frequency/temperature
Accuracy5½ digits
Sampling rateup to 50 kS/s
A precision benchtop multimeter for electrical characterization and sensor calibration.
Driver module: Keithley2110
from quatm.drivers.Keithley.multimeter import Keithley2110

dmm = Keithley2110(addr="USB0::0x05E6::0x2110::1234567::INSTR")

# DC voltage measurement
voltage = dmm.measure_voltage(range="10V")

# 4-wire resistance measurement
resistance = dmm.measure_resistance_4wire(range="100Ω")

# Temperature (thermocouple)
temp = dmm.measure_temperature(tc_type="K")

Voltcraft VC7055 two-channel benchtop multimeter

VC7055 Voltcraft
Communicationserial (115200 baud, SCPI)
Channels2
FunctionsDCV/ACV/DCI/ACI/resistance/frequency/capacitance
Bluetoothoptional (VC7055BT)
A two-channel benchtop multimeter; the BT variant with built-in Bluetooth supports wireless data acquisition.
Driver module: VC7055
from quatm.drivers.Voltcraft.multimeter import VC7055

dmm = VC7055(addr="/dev/ttyUSB0")

# Simultaneous two-channel measurement
v1 = dmm.measure_voltage(channel=1)
v2 = dmm.measure_voltage(channel=2)

Device configuration

"daq": {
    "type": "local",
    "module": "drivers.NI.daq",
    "class": "NIDAQ",
    "arguments": {"dev_name": "Dev1", "rate": 100_000},
},
"coil_ps": {
    "type": "local",
    "module": "drivers.ElektroAutomatik.base",
    "class": "PS2000B",
    "arguments": {"conn": "/dev/ttyUSB0", "remote_mode": True},
},
"dmm": {
    "type": "local",
    "module": "drivers.Keithley.multimeter",
    "class": "Keithley2110",
    "arguments": {"addr": "USB0::..."},
},

4.6 - Environmental monitoring

Drivers for temperature, vacuum and cryogenic monitoring: Lakeshore, Cryocon, Pfeiffer, Leybold.

Overview

Laboratory environmental monitoring underpins the stability of quantum experiments. Quatm ships drivers for temperature, vacuum and cryogenic level monitoring with real-time data logging and anomaly alerts.


Lakeshore 218 temperature monitor

218 Lakeshore
Communicationserial (9600 baud, 7E1)
Channels8
Sensorsdiode (Si/GaAs), platinum resistance (Pt100/1000), Cernox, RuO₂
Temperature1.4 K – 1500 K (depends on sensor)
The standard temperature monitor for cryogenic physics experiments. The eight channels are configured independently and support diode and resistance thermometers as well as Cernox thin-film resistors.
Driver module: Lakeshore218
from quatm.drivers.Lakeshore.base import Lakeshore218

ls = Lakeshore218(conn="/dev/ttyUSB0")

# Configure channel 1 as a silicon diode
ls.set_sensor_type(channel=1, sensor_type="DT-670")

# Read temperature
temp = ls.get_temperature(channel=1)   # Kelvin
all_temps = ls.get_all_temperatures()  # All 8 channels

# Read raw sensor values (voltage/resistance)
raw = ls.get_reading(channel=1)

Cryocon 12C / 14C / 18C temperature controllers

12C / 14C / 18C Cryocon
Communicationserial (9600 baud)
Channels2–4 (depends on model)
Supported sensorsdiode/Pt/Cernox
Cryogenic temperature controllers with PID heater output for precise temperature stabilization.
Driver module: Cryocon1x
from quatm.drivers.Cryocon.base import Cryocon1x

cryo = Cryocon1x(conn="/dev/ttyUSB0", nchannels=4)

# Read channel 1 temperature
temp = cryo.get_temperature(channel=1)

# PID setpoint
cryo.set_setpoint(channel=1, temperature=4.2)  # 4.2 K
cryo.set_heater_range(channel=1, range="low")
cryo.enable_heater(channel=1, enable=True)

Lumel RE72 · Omron E5_C temperature controllers

RE72 / E5_C Lumel / Omron
CommunicationRS485 Modbus RTU
FeaturesPID autotuning, ramp/soak programs
Applicationoptical table temperature stabilization, laser crystal temperature control
Industrial-grade PID temperature controllers communicating over the Modbus RTU protocol. Commonly used for laser crystal temperature control and optical table environmental temperature control.
Driver module: LumelRE72Controller / OmronE5xCController
from quatm.drivers.Lumel.base import LumelRE72Controller
from quatm.drivers.Omron.base import OmronE5xCController

# Lumel RE72
lumel = LumelRE72Controller(conn="/dev/ttyUSB1", daddr=1)
temp = lumel.get_temperature()
lumel.set_setpoint(25.0)

# Omron E5_C
omron = OmronE5xCController(conn="/dev/ttyUSB1", daddr=2)
omron.set_setpoint(30.0)
omron.enable_heater(True)

Pfeiffer TPG2xx high-vacuum gauges

TPG2xx series Pfeiffer Vacuum
Communicationserial (9600 baud)
Channels2
Supported gaugesPirani / Cold Cathode / combination gauge
Rangeatmospheric pressure – 10⁻¹⁰ mbar
A compact two-channel vacuum gauge controller supporting both rough vacuum (Pirani) and ultra-high vacuum (cold cathode) gauge heads.
Driver module: TPG2xx
from quatm.drivers.Pfeiffer.base import TPG2xx

tpg = TPG2xx(conn="/dev/ttyUSB0")

# Read both pressure channels
p1 = tpg.get_pressure(channel=1)  # mbar
p2 = tpg.get_pressure(channel=2)

# Gauge type identification
gauge_type = tpg.get_gauge_type(channel=1)

Leybold ITR wide-range vacuum gauges

ITR series Leybold
Communicationserial (9600 baud, binary protocol)
Rangeatmospheric pressure – 5×10⁻¹⁰ mbar
Outputcontinuous pressure data stream
Wide-range Pirani/cold cathode combination vacuum gauges that actively push pressure data.
Driver module: GenericITR
from quatm.drivers.Leybold.base import GenericITR

itr = GenericITR(conn="/dev/ttyUSB0")
pressure = itr.get_pressure()  # mbar

Agilent XGS-600 multi-channel vacuum controller

XGS-600 Agilent
Communicationserial (RS485)
Channelsup to 6 gauge heads
Gauge typesHot Filament / Cold Cathode / Pirani / Convection
A multi-gauge controller for large vacuum systems, addressed over RS485 in a daisy chain.
Driver module: XGS600
from quatm.drivers.Agilent.pressure import XGS600

xgs = XGS600(conn="/dev/ttyUSB0", addr=1)

# Read all channel pressures
pressures = xgs.get_all_pressures()
p1 = xgs.get_pressure(channel=1)

Cryogenic level monitoring

Cryomagnetics LM500 liquid helium/nitrogen level meter

LM500 / LM510 Cryomagnetics
Communicationserial (9600 baud)
FunctionsHe/N₂ level, temperature, sample chamber temperature
Channels1–2 level + 4 temperature
The standard instrument for monitoring levels in superconducting magnet dewars. It monitors liquid helium/nitrogen levels and the sample chamber temperature simultaneously.
Driver module: LM500
from quatm.drivers.Cryomagnetics.base import LM500

lm = LM500(conn="/dev/ttyUSB0")

helium_level = lm.get_helium_level()    # %
nitrogen_level = lm.get_nitrogen_level() # %
sample_temp = lm.get_temperature()

American Magnetics 1700 level monitor

1700 American Magnetics
Communicationserial or TCP/IP (port 7180)
FunctionsHe / N₂ level, relay control
A level monitor supporting both serial and network interfaces.
Driver module: AM1700
from quatm.drivers.AmericanMagnetics.base import AM1700

am = AM1700(conn="192.168.1.50")  # TCP/IP
# Or serial port: am = AM1700(conn="/dev/ttyUSB0")
level = am.get_helium_level()

Generic environmental monitoring configuration

"chamber_pressure": {
    "type": "local",
    "module": "drivers.Pfeiffer.base",
    "class": "TPG2xx",
    "arguments": {"conn": "/dev/ttyUSB0"},
},
"cryo_temp": {
    "type": "local",
    "module": "drivers.Lakeshore.base",
    "class": "Lakeshore218",
    "arguments": {"conn": "/dev/ttyUSB1"},
},

4.7 - Experiment control and others

Drivers for FPGA controllers and auxiliary devices such as ADwin, NI-FPGA and Red Pitaya, covering the full spectrum of experiment control requirements from nanosecond real-time timing to general-purpose switching logic.

Overview

FPGA controllers and auxiliary devices are an essential part of experiment automation: from nanosecond real-time timing to relay switching, Quatm provides a unified control interface. ADwin, NI-FPGA, Red Pitaya and several other brands are currently supported.


ADwin

Overview

ADwin is a family of real-time controllers from Jaeger Computergesteuerte Messtechnik, based on an FPGA/DSP architecture, that executes real-time tasks independently of the PC operating system.

ADwin Gold II / Pro II
CommunicationPCI / USB
ProcessorDSP + FPGA
Timing precisionnanosecond level
Analog input16-bit
Digital I/OTTL (5V)
Memory32 MB DRAM
It runs real-time processes compiled from ADbasic and communicates with Quatm through shared memory or register reads and writes. Typical applications include laser cooling timing, MOT magnetic field switching and photon counting acquisition.
Driver module: ADwin

How it works

PC (Quatm)                    ADwin (real-time)
┌──────────┐    PCI/USB    ┌──────────────┐
│ experiment│ ←──────────→ │ ADbasic proc │
│   .py     │  Par/Data    │ ns timing+acq│
└──────────┘               └──────────────┘

Quatm experiment scripts read and write parameters and data arrays through the ADwin driver, interacting with the ADbasic process running on the ADwin. The timing-critical parts execute on the ADwin hardware, while the Python side handles parameter setting and result readout.

Initialization

from quatm.drivers.fpga import ADwin

# Connect to ADwin (PCI bus)
adw = ADwin(device_number=1)

# Load and start the ADbasic process
adw.load_process("experiment_sequence.TB1")
adw.start_process()

# Set experiment parameters
adw.set_par(1, 100_000)     # Par_1 = 100 µs cooling time
adw.set_par(2, 50)          # Par_2 = 50 cycles
adw.set_par(3, 10_000)      # Par_3 = 10 ms detection time

# Start one experiment sequence (triggered by an event in ADbasic)
adw.trigger()

# Read acquired data
photons = adw.get_data(1, data_length=1000)  # Read photon counts from Data_1

Key methods

MethodDescription
load_process(binary_path)Load an ADbasic-compiled binary process
start_process()Start the loaded process
stop_process()Stop the running process
set_par(index, value)Write an integer parameter (1–80)
set_fpar(index, value)Write a floating-point parameter (1–80)
get_par(index)Read an integer parameter
get_data(index, data_length)Read a data array (1–200)
set_data(index, values)Write a data array
trigger()Send a software trigger event

Typical ADbasic process structure

' ADbasic code snippet — experiment timing
Init:
  Par_1 = 100000   ' Cooling time (µs)
  Par_2 = 50       ' Number of cycles
Event:
  For i = 1 To Par_2
    P2_DAC(1, 5000)   ' Set AOM power
    P2_DAC(2, 800)    ' Set magnetic field
    ' ... wait, acquire, switch ...
  Next i

Device configuration

"fpga": {
    "type": "local",
    "module": "quatm.drivers.fpga",
    "class": "ADwin",
    "arguments": {"device_number": 1},
}

Exception:

  • quatm.drivers.fpga.adwin.AdWinDriverError(message)

    class quatm.drivers.fpga.adwin.AdWinDriverError(Exception):
        pass
    

quatm.drivers.fpga.client module

Class:

  • quatm.drivers.fpga.client.AdWinClient(experiment, simulating=None)

    class quatm.drivers.fpga.client.AdWinClient(object):
        def filter_cmdlist(self, combinetime=1e-08):
            pass
    
        def push(self, time, command, value, combinetime=1e-09):
            pass
    
        def start_realtime(self, processortime=0, repetitions=1):
            pass
    
        def stop(self):
            pass
    

quatm.drivers.fpga.units module


NI-FPGA LabVIEW FPGA modular instruments

PXIe-78xxR / PCIe-78xxR series NI-FPGA
CommunicationNI-FPGA Interface Python API
FPGAXilinx Kintex/Virtex
Digital I/Oup to 1 Gb/s
Analog I/Oup to 3.2 GS/s
ProgrammingLabVIEW FPGA + Python runtime control
Controls LabVIEW FPGA bitstreams at runtime through the nifpga Python API. Suited to custom digital signal processing requiring very high throughput and deterministic timing.
Driver module: ni_fpga
from quatm.drivers.NI.ni_fpga import NIFPGA

fpga = NIFPGA(resource_name="RIO0", bitfile="custom_logic.lvbitx")
fpga.run()
fpga.write_control("pulse_width", 100)        # ns
fpga.write_control("trigger_delay", 50)
fpga.read_indicator("photon_count")

Conrad relay board

Relay Board Conrad
Communicationserial (19200 baud)
Channels8 relays per board
Supportdaisy-chain expansion
An economical multi-channel relay controller, suited to magnetic trap coil polarity switching, device power switching and laser shutter control.
Driver module: RelayBoard
from quatm.drivers.Conrad.base import RelayBoard

rb = RelayBoard(conn="/dev/ttyUSB0", start_addr=0)
rb.set_relay(relay=0, state=True)
rb.set_relay(relay=3, state=False)
rb.write_byte(0b10101010)

5 - Data and analysis

Data streams, HDF5 acquisition, imaging and fitting.

Data path

Experiment runs publish typed data and image streams over ZeroMQ. Data clients subscribe to the channels they need; the Data Manager accumulates runs, writes HDF5 files and exposes live series to the GUI and web console. The message and property Hubs provide the same data to every client, so a plot, a notebook and a remote browser see one coherent experiment.

HDF5 structure

A run is archived with:

  • the experiment and task identifiers;
  • parameter values and scan definition;
  • DAC/TTL settings and sequence metadata;
  • scalar data series and image data;
  • save-acknowledgement status and error information.

The file is self-describing: a run can be reconstructed and compared without the original GUI session.

Analysis modules

Quatm includes reusable analyses for common quantum-physics measurements:

  • absorption imaging and optical density;
  • ion / bright-spot detection;
  • 1D and 2D Gaussian fits;
  • skewed-Gaussian and multi-peak fits;
  • ROI and line-profile extraction;
  • averaging and histogram utilities.

Analysis can run from the Data Manager, from Python scripts, or on the analysis machine while acquisition continues. The same modules are used in the desktop GUI and in the web console.

Live monitoring

The Stream Monitor and image viewers follow data as it arrives. The history viewer, timeline and live plots are read-only clients: they cannot alter the experiment state. This makes them safe to open during operation and on remote machines.

Export

Data and fit results can be exported to CSV, images or publication-quality PDF. Export runs off the GUI thread, so large datasets do not freeze the interface.

5.1 - 2D Gaussian fit

Two-dimensional Gaussian fit within the ROI of each frame of an image stream: outputs the fit parameters and 1/e and 1/e² contour masks.

Overview

quatm/analysis/2D_gauss_fit.py provides 2D Gaussian fit analysis (TwoDGaussFit): it subscribes to an image stream and, on receiving an image, performs a 2D Gaussian fit inside the ROI (astropy Gaussian2D + Const2D, Levenberg-Marquardt iterations), publishes the fit parameters to the data stream and sends 1/e and 1/e² contour masks back to the image stream.

A fast variant, quatm/analysis/2D_gauss_fit_fast.py, is also available: the core fit is implemented in a Cython extension, and the interface and functionality are identical.

Output parameters: center x0 / y0, widths sigma_x / sigma_y, amplitude and background.

Usage

from quatm.analysis.2D_gauss_fit import TwoDGaussFit

fitter = TwoDGaussFit(name="gauss2d")   # subscribe to the imagestreams image stream
fitter.run()

The analysis process is started from the Analysis Manager (AnalysisManager), where it is selected with a checkbox.

5.2 - 1D Gaussian fit

Fits a Gaussian to each frame of 1D data in a data stream and publishes the fit parameters and fitted curve.

Overview

quatm/analysis/gaussfit.py provides 1D Gaussian fit analysis (Gaussfit): as a standalone analysis process it subscribes to data streams, fits a Gaussian to each frame of 1D data and publishes the results to the data stream.

The fit function has the form:

f(x) = A0 / (sqrt(2*pi) * sigma) * exp(-(x - pos)^2 / (2*sigma^2)) + offset

The output parameters are A0 (area), sigma (width), pos (center), offset (background) and height (peak height = A0/(sqrt(2π)·σ)).

Configuration properties

PropertyDescription
datastreamsList of input data streams (each frame may be a 1D array, or a 2D array with two rows x/y)

Usage

from quatm.analysis.gaussfit import Gaussfit

gf = Gaussfit(name="gauss")   # name serves as the property/data-stream namespace
gf.run()

The analysis process is started from the Analysis Manager (AnalysisManager), where it is selected with a checkbox; input streams and parameters are configured in the property tree.

5.3 - Data management and analysis module

Data aggregation and storage management: DataSummary collects per-run data and writes HDF5, with a companion h5 management interface.

Overview

The data management and analysis module aggregates the data streams produced by an experiment into a complete record for each run and persists them. DataSummary in quatm/servers/datamgr.py is the aggregation core: it subscribes to a number of data channels plus experiment start/end events, organizes the data of each run into a complete dictionary that can be written to HDF5, read back and cleared. quatm/GUI/h5storage/ provides the data-management interface built on it (subscription, periodic saving and plotting).

Core components

ComponentModuleDescription
DataSummaryservers/datamgr.pyAggregates multi-channel data streams into a complete dictionary for each run
H5StorageDataMgrGUI/h5storage/data_manager.pyHost for the aggregate/single-stream/image data managers
H5StorageGuiGUI/h5storage/main_window.pyData management main window (data/plot/event tabs)
H5DataPlotter / EventPlotterGUI/h5storage/plotters.pyMeasurement data / event data plotting

Key methods (DataSummary)

MethodDescription
initDataq(Re)subscribes to the channels configured in properties and to the experiment start/end events
recvData / processIncomingDataReceives data and merges it into the most recent run record
savetoFileWrites the collected run dictionaries to a structured HDF5 dataset
loadFromFileReads a set of stored run dictionaries back from HDF5
clearClears all collected data
run_foreverBlocking loop that keeps receiving data

Usage

from quatm.servers.datamgr import DataSummary

summ = DataSummary(name="sum")
summ.initDataq()      # subscribe to the channels configured in the properties
summ.run_forever()    # blocking acquisition; savetoFile then writes the run dict to the .h5

Input channels and the save path are configured in the property tree; for the GUI side see Data storage.

5.4 - Data stream subscription management

Main scrolling data-view window: ScrollPlot subscribes to data streams and scrolls them; PlotDataEditor selects the data source.

Overview

quatm/GUI/entries/scrollwindow.py provides data stream subscription management (ScrollWindow): a multi-channel scrolling data-view main window that lays out ScrollPlot subplots by count (two per row) and uniformly distributes data blocks to the corresponding subplots and refreshes them; PlotDataEditor provides drop-down selection of data/image streams.

Components

ComponentDescription
ScrollWindowMain window: creates subplots by count, distributes data and refreshes
ScrollPlotScrolling subplot: subscribes to data streams, ring buffer + periodic refresh
PlotDataEditorData-source selection panel: drop-down candidates written back to the property ydata

Key methods

MethodDescription
initPlots / feedData / updatePlot (ScrollWindow)Create subplots / feed data / batch refresh
setnewData (ScrollPlot)Periodically pulls data and refreshes the curve
dataSelectDialog / subscribe_window / configureWindow (ScrollPlot)Select channels / change subscription / configure this plot
updateDatachannel (PlotDataEditor)Writes the selected stream into the property ydata when the drop-down changes

Usage

from quatm.GUI.entries.scrollwindow import ScrollWindow

win = ScrollWindow(name="PMT scroll monitor", parent=parent, n_plots=2)

Data-stream subscription, channel selection and configuration are performed on each subplot through its context menu; for a more complete description see Scrolling waveform window.

5.5 - Graphical ROI manager

Remotely controllable ROIs whose position, size and color are synchronized through the central property store.

Overview

quatm/GUI/widgets/ROI.py provides ROI: a subclass of ROI that communicates its color, position and size through the central property store under a unique name. It is suited to displaying and managing multiple regions of interest in a GUI and letting remote clients view and adjust them in sync.

Methods

MethodDescription
update_from_propertiesRefreshes the ROI pen color and geometry from the color/position/size in properties

Usage

from quatm.GUI.widgets.ROI import ROI

roi = ROI(name="analysis_roi", parent=view)   # name must be unique; duplicate names share properties
roi.update_from_properties()

For a more complete description, see Remote ROI.

5.6 - Live updating plot

Real-time plotting that subscribes to data streams and keeps updating: the UpdatingPlot and LivePlot containers.

Overview

quatm/GUI/entries/updating_plot.py provides the live updating plot: UpdatingPlot is a subclass of PlotItem that subscribes to data streams and periodically pulls new data to refresh the curve; LivePlot, by contrast, is a widget container that embeds an UpdatingPlot and can carry an image-stream name, which makes it convenient to embed as a dock panel in other windows.

Components

ComponentDescription
UpdatingPlotLive plot item: subscribes to data streams and keeps updating the curve
LivePlotWidget container: embeds one UpdatingPlot and can carry an image stream

Key methods (UpdatingPlot)

MethodDescription
setnewDataPeriodically pulls new data; for 1D arrays it automatically adds an x axis and then updates the curve
updateConfigurationCancels the old subscriptions, resubscribes according to the stream list configured in properties and rebuilds the curves
subscribe_windowOpens the subscription editor to select data streams and reapply the configuration
configureWindowOpens the property-edit dialog to adjust display settings

Usage

from quatm.GUI.entries.updating_plot import UpdatingPlot, LivePlot

plot = UpdatingPlot(name="PMT signal")        # used directly as a plot item
host = LivePlot(name="PMT signal", parent=parent)  # or embedded as a container in the UI

For a more complete description, see Live plotting.

5.7 - Subscription editor

Manages the program’s subscriptions to data/image streams: display, add, delete and hot property updates.

Overview

quatm/GUI/editors/subscription_editor.py provides the subscription editor (SubscriptionEditor): it manages the program’s subscriptions to data or image streams. The interface shows the current subscription list, offers a drop-down to select a new stream and add it, and allows subscriptions to be deleted; changes are synchronized immediately to the property store and a signal is broadcast, so that plotting/monitoring components can update on the fly.

Key methods

MethodDescription
loadSubscriptionsReads the saved subscriptions from the property store and loads them into the list
handle_property_changesListens for property changes: when this subscription key changes, refreshes the list and broadcasts
update_streamlistRebuilds the drop-down candidates from the currently available data-stream list
addAdds the stream selected in the drop-down to the subscriptions and synchronizes properties
deletemarkedRemoves the subscriptions marked for deletion and synchronizes properties

Usage

from quatm.GUI.editors.subscription_editor import SubscriptionEditor

editor = SubscriptionEditor(props=props, category="Data", parent=parent)

Constructor parameters: category is Image / Data / List; propprefix, streamkey and fullList (List mode) can be used to adjust the property keys and candidate streams. For a more complete description see Subscription editor.

5.8 - Table widget

Displays and edits parameter types holding NumPy record arrays in tabular form.

Overview

quatm/GUI/widgets/table_parameter.py provides the table widget (table_parameter): it extends the parameter tree so that parameters holding NumPy record arrays can be displayed and edited in tabular form, and can be registered as a reusable custom parameter type via registerParameterType.

Components

ComponentDescription
table_parameterItemTable-widget parameter item for editing record arrays (a WidgetParameterItem subclass)
table_parameterTable parameter type (a Parameter subclass), used together with the item above

Key methods

MethodDescription
makeWidget (table_parameterItem)Creates a QTableWidget editor from the recArray dtype
value / setValue (table_parameterItem)Reads back cell by cell from the table / writes the array cell by cell
valueChanged (table_parameterItem)Refreshes the table and label when the parameter value changes
setValue / valueIsDefault (table_parameter)Sets the parameter value / tests whether it is the default value

Usage

from quatm.GUI.widgets.table_parameter import table_parameter

param = table_parameter(name="records", value=recarray)   # insert into the parameter tree

For a more complete description see Table parameter.

5.9 - Data, image and camera editor

Display and editing of data-stream, image and camera properties: these capabilities are now provided by parameter boxes, the image monitor and the property editor.

Overview

An earlier unified interface provided a set of widgets, ParameterBox / ImageDataBox / CamPropsBox / DataBox, for displaying a single data-stream value, image data and camera properties as compact boxes, with right-click subscription and data selection. As the GUI was split up and evolved, these capabilities have been taken over by the current components:

Legacy componentFunctionCurrent implementation
ParameterBoxCompact display of a single data-stream valueParameter box widgets (widgets/arg_boxes etc.)
ImageDataBoxCompact box for image data displayImage monitor (entries/image_monitor.ImageDisplay)
CamPropsBoxDisplay and editing of camera propertiesProperty tree editor (PropEdit) + subscription
DataBoxSimple label-value boxGUI base frames (BFrame / BWidget)

5.10 - Image monitor

Automatically updating image display window: subscribes to image/mask streams, with noise reduction, bright-spot detection and saving.

Overview

quatm/GUI/entries/image_monitor.py provides the image monitor (ImageDisplay): an automatically updating image display window that periodically pulls the latest frame from the image streams subscribed in properties, transforms it according to resolution and offset, refreshes the display and synchronously overlays mask streams. It has built-in noise reduction, bright-spot detection, saving of the current frame and property configuration (display levels, ROI, etc.).

Methods

MethodDescription
update_image / update_maskPeriodically pulls the image/mask stream and refreshes the display and overlay
noise_filterBilateral filtering + median filtering noise reduction using property parameters
spot_detectorBinarizes the image by threshold to detect bright spots
saveCurrentImageSaves the currently displayed frame as PNG (named by time)
subscribe_window / subscribe_maskOpens the subscription dialog to select image/mask streams and resubscribes
configureWindowOpens the property-edit dialog to adjust display levels, ROI, etc.

Usage

from quatm.GUI.entries.image_monitor import ImageDisplay

view = ImageDisplay(imagestreams=["camera/image"], parent=parent)
view.configureWindow()   # adjust display levels, ROI, etc.

For a more complete description, see Image monitor.

5.11 - Image viewer

Main scientific image-viewing window: a central image display with row/column line-profile and histogram color-scale panels.

Overview

quatm/GUI/entries/image_group.py provides the image viewer window (ImageWindow): with a central ImageDisplay image display as the main widget and docked panels for row/column line profiles and the histogram color scale around it, it is used for live monitoring and analysis in imaging experiments.

Layout structure

Dock areaWidgetPurpose
CenterImageDisplaySubscribes to image streams and refreshes automatically; masks can be overlaid
TopLivePlot (row profile)Intensity distribution for image rows
RightLivePlot (column profile)Intensity distribution for image columns
LeftHistogramLUTWidgetHistogram color scale: level range and false-color mapping

Display parameters (in the Viewer/ namespace: min/max levels, gradient, tick_position) are restored from properties on construction and saved on close.

Usage

from quatm.GUI.entries.image_group import ImageWindow

win = ImageWindow(name="absorption_imaging")   # standalone top-level window
win.show()

For a more complete description, see Image viewer.

5.12 - Live data monitor

Real-time plotting of measurement data: data sources / multiple y axes, filtering/binning/fitting and export.

Overview

quatm/GUI/alignment/plotter.py provides the live data monitor (DataPlotterWidget): it plots the data acquired during a measurement. It supports switching the data source and provides an x-axis selector and several y-axis selectors; each y axis is processed through a configurable data chain (scale factor → filter → binning/fitting) before being drawn in the plot area, and the result can be exported to PDF.

Components

ComponentDescription
DataPlotterWidgetMain plot: data-source drop-down, x-axis selector, several y-axis selectors (right-click toggles the configuration panel)
YAxisWidgetHelpery-axis data chain: scale factor, filter, binning and fitting

Key methods

MethodDescription
setDataSourceSets or changes the data source
checkForNewData / updateDatasourcesChecks for new data / new data sources and refreshes
plotDataAdds a set of x/y data to the plot area as a new curve
toggleConfigToggles display of the data-source and y-axis configuration panel
exportpdfExports the current plot as an archival-quality PDF
clearClears the data summary and all curves in the plot area

Y-axis chain (YAxisWidgetHelper): updateYscale sets the scale factor; the context menu offers addBinning for binning and addFitting for fitting (e.g. Gaussian/linear), and reevaluateData re-runs the whole data chain after changes such as filters.

Usage

from quatm.GUI.alignment.plotter import DataPlotterWidget

plotter = DataPlotterWidget(
    axis_name="myaxis",
    prop_root="...",
    datastream_names=[".../data"],
    n_yaxis=1,
    parent=parent,
)
plotter.toggleConfig()   # expand the data-source / x, y axis selection config

For a more complete description, see Auxiliary plotting.

5.13 - Property editor

A tool for viewing and editing the property tree, based on ParameterTree.

Overview

quatm/GUI/entries/property_editor.py provides the property editor: it presents the key/value tree of the property store as an expandable, editable parameter tree, and modifications are written back immediately. It supports browsing by subtree, searching, saving/loading and deleting the selected nodes.

Components

ComponentDescription
TreeEditExtracts information from Properties and organizes it into a tree-view structure
PropEditProperty editing main window (ParameterTree): subtree browsing, search, save/delete
ScalableGroupSpecial parameter group: automatically generates two mutually reciprocal child parameters
PropSelectorKey-list selector: checks keys from the property tree and maintains a key list

Usage

from quatm.GUI.entries.property_editor import PropEdit

editor = PropEdit(name="device_status", props=props, subtree="/", parent=parent)

Use subtree to restrict the display to properties under a given path. For a more complete description, see Property editor.

5.14 - Property update manager

Property timestamp watchdog: checks whether properties keep updating and raises an alarm in Deadman fashion after a timeout.

Overview

quatm/analysis/props_check.py provides the property update manager (PropsCheck): a watchdog that runs as a standalone analysis process and periodically reads the timestamp property specified by the property path _prop_path_abs; if it has not been updated for longer than _deadman_delay_sec, an alarm is raised through send_error (Mattermost can be configured).

Key methods

MethodDescription
runPeriodically checks whether the timestamp property is fresh and raises an alarm in Deadman fashion on timeout

Usage

from quatm.analysis.props_check import PropsCheck

pc = PropsCheck(name="props_watchdog")   # property namespace
pc.run()

On the GUI side there is also a property checker (PropertiesChecker) for the same purpose, which verifies that the property center is really distributing updates (it sets a flag and reads it back; if the update has not propagated, it shows a dialog and raises an alarm), see Property checker.

5.15 - Skewed Gaussian fit

Fits a skewed Gaussian (skew-normal) distribution to 1D data in a data stream, additionally reporting skewness and peak position.

Overview

quatm/analysis/gaussfit_skewed.py provides skewed Gaussian fit analysis (GaussfitSkewed): it fits a skew-normal distribution to each frame of 1D data in a data stream and publishes the results to the data stream. Compared with an ordinary Gaussian fit, the skewed Gaussian describes asymmetric (tailed) peak shapes better, and the results additionally include the skewness parameter and the peak position.

Usage

from quatm.analysis.gaussfit_skewed import GaussfitSkewed

gs = GaussfitSkewed(name="skewed")   # name serves as the property/data-stream namespace
gs.run()

The input data streams are configured in the property datastreams; the analysis process is started from the Analysis Manager (AnalysisManager), where it is selected with a checkbox.

5.16 - Data and image stream monitor

Real-time scrolling plot window: subscribes to data/image streams and scrolls them, with running mean and right-click configuration.

Overview

quatm/GUI/entries/live_plot.py provides data-stream/image-stream scrolling monitoring (ScrollPlot): inside the multi-subplot scrolling window ScrollWindow, each ScrollPlot subscribes to several channels of one data stream, maintains a ring buffer and scrolls scatter points and a moving-average curve; PlotDataEditor provides a data-source selection drop-down.

Components

ComponentDescription
ScrollWindowMulti-subplot scrolling main window: restorePlots restores / addPlot appends subplots
ScrollPlotScrolling subplot: ring buffer, scatter and moving average, context-menu operations
PlotDataEditorData-source selection panel: drop-down candidates written back to properties/the parent plot

Key methods (ScrollPlot)

MethodDescription
feedData / setnewDataPushes a new data block into the buffer / periodically pulls and refreshes
updatePlot / clearPlotRefreshes the curve / clears and rebuilds this plot
running_meanEfficiently computes the N-point moving average using cumulative sums and differences
dataSelectDialog / subscribe_window / configureWindowSelect channels / change subscription / configure
deletePlot / updateTitleRemoves this plot / synchronizes the title and data source

Usage

from quatm.GUI.entries.live_plot import ScrollWindow

host = ScrollWindow(name="PMT scroll monitor", parent=parent, n_plots=2)

For a more complete description, see Live scrolling plot.

5.17 - Absorption imaging

Computes the optical density (column density) from with-atom, without-atom and background frames using the Beer-Lambert law.

Overview

quatm/analysis/imagedivider.py provides absorption imaging analysis (ImageDivider): it collects with-atom (atom), without-atom (no-atom) and background (background) images by imgindex, combines them with physical parameters such as the scattering cross-section, and computes and publishes the optical density (two-dimensional atomic column density) according to the Beer-Lambert law.

The intensity relation is I = I₀·exp(−n·σ), so the column density is n = ln(I₀/I)/σ; physical parameters such as detuning and linewidth are updated through event messages when the experiment starts.

Configuration properties

PropertyDescription
_atomindex / _noatomindex / _backgroundindeximgindex of the with-atom / without-atom / background frames
Detuning / linewidth, etc.Imaging laser and transition parameters (updated by experiment events)

Usage

from quatm.analysis.imagedivider import ImageDivider

div = ImageDivider(name="od")   # subscribe to the corresponding image streams
div.run()

The analysis process is started from the Analysis Manager (AnalysisManager), where it is selected with a checkbox.

5.18 - Ion detection series

Bright-spot/single-ion detection and ion-state discrimination: the BrightSpots, SingleIonPresent and IonState analysis processes.

Module overview

ModuleClassDetection methodPurpose
ion_blobBrightSpotsThreshold segmentation + connected-component countingCounts the number of ions in each frame
bright_spotsBrightSpotsskimage blob_dogDetects bright spots and outputs a position mask and bloblist
single_ion_presentSingleIonPresentCounting + multiple Gaussian-fit-parameter criteriaDecides whether a single ion is present
ion_stateIonStateMulti-image sequences by imgindexDiscriminates the ion product state

Discrimination workflow

IonState collects the check / survival / hot / shelved image series by imgindex, detects whether an ion is present in each image, and aggregates the results into the product state:

group frames by imgindex (check / survival / hot / shelved)
        ↓ detect the ion in each frame by blob analysis
     aggregate → publish the product-state result

Usage

from quatm.analysis.ion_state import IonState

det = IonState(name="ionstate")   # subscribe to the image streams and group by imgindex
det.run()

Each process is started from the Analysis Manager (AnalysisManager), where it is selected with a checkbox; image streams, thresholds, imgindex, etc. are configured in the property tree.

5.19 - Image processing tools

Image processing analysis processes for hot-pixel filtering/finding, rotation, slicing, frame selection and background subtraction.

Module list

The following are all standalone analysis processes that subscribe to image streams, located in quatm/analysis/:

ModuleClassDescription
hot_pixel_filterHotFilterFilters/replaces hot pixels according to the coordinate list in the property hotpixels and sends the result back
hot_pixel_finderHotFilterFinds hot-pixel coordinates statistically and writes them to properties (for use by the filtering script)
rotate_imageImageRotateRotates the image by an angle and rotates the coordinate offsets accordingly
imagesliceImageSliceComputes row/column projections (sum/clip/integrate) over the ROI and sub-region images
imageselectorImageSliceSelects the frame with the specified imgindex from the image stream and forwards it
substract_imageImageSubstractSubtracts a fixed background image (automatically adapting for transposition) and outputs the difference

Usage

from quatm.analysis.hot_pixel_filter import HotFilter

f = HotFilter(name="hotfix")   # subscribe to the image stream and filter with the hot-pixel list from the properties
f.run()

Each process is started from the Analysis Manager (AnalysisManager), where it is selected with a checkbox; input streams and parameters are configured in the property tree.

6 - GUI components

Desktop applications and their responsibilities.

The desktop suite is built with PySide6 and shares a common visual language, theme and lifecycle model.

Architecture overview

The Experiment Browser is the main window: FileSelector opens experiment scripts, the central QMdiArea hosts one or more ExperimentWindow panes, and the top dock area manages the experiment queue, preparation station and looper. ExperimentManager is the execution engine that consumes tasks and drives each run through its lifecycle.

flowchart TB
    subgraph Browser[Experiment Browser - main window]
        FS[FileSelector<br/>experiment file tree]
        MDI[QMdiArea<br/>experiment pane container]
        EW[ExperimentWindow<br/>single-experiment control]
        DOCKS[Top dock area<br/>queue / preparation / looper]
        FS --> MDI
        MDI --> EW
        DOCKS --> MDI
    end

    EM[ExperimentManager<br/>execution engine]
    QUEUE[Experiment queue / scans]
    PROC[Process Manager]

    EW --> EM
    DOCKS --> EM
    EM --> QUEUE
    EM --> PROC

ExperimentWindow owns the parameter panel, submission and scan entry points. ExperimentManager does not draw the UI; it consumes tasks from the queue and tracks their run state. The Process Manager starts and supervises the independent GUI, driver and service processes.

ApplicationResponsibility
Operations ConsoleServices, tasks/processes/ports and device health in one window
Experiment BrowserExperiment selection, parameters, queue, scans, preparation and looper
DashboardInstrument defaults and parameter tuning by device group
Data ManagerHDF5 browsing, fits, image analysis and export
Stream MonitorLive data and message inspection
Image / Live / Scroll viewersCamera, ROI and real-time curve displays
TimelineFPGA/ADwin sequence visualisation
Device ManagerDevice database editing, driver catalogue and health checks
Property / Subscription / Auto-save editorsShared property tree and persistence policies
Settings HubNotifications, licence and low-frequency configuration
Task & Process MonitorRead-only task, process, endpoint and health observability

Design conventions

  • Every window can be opened independently or through the launcher.
  • Closing the launcher’s last window prompts for full application exit and stops all services.
  • Windows that create background work release it in closeEvent; hidden windows pause refresh timers rather than stopping data clients.
  • Help and About pages are per-window and bilingual.
  • The interface follows the operating system’s font scaling and light/dark appearance.

Extending the GUI

Reusable building blocks live under quatm/GUI/widgets and quatm/GUI/infra. Project-specific windows belong in the project’s GUI/ directory and are registered in configfile.json. The application identity table controls window names, icons and desktop launchers.

6.1 - Experiment browser

The Quatm main window: experiment browsing, queue management and a multi-window MDI workspace.

Overview

quatm/GUI/browser/main_window.py provides the experiment browser (Browser): Quatm’s main application window, built on QMainWindow + QMdiArea as a multiple-document interface. The file selector on the left browses experiment scripts, the experiment queue, preparation station and looper are docked at the top, and each experiment occupies its own sub-window in the central MDI area.

Layout

┌───────────────┬───────────────────────────────┐
│ FileSelector  │   QMdiArea (subwindow area)   │
│ (file tree)   │   ┌─────────────────────┐     │
│               │   │ ExperimentWindow    │     │
├───────────────┤   └─────────────────────┘     │
│ Queue / Prep  │                               │
│ / Looper dock │                               │
└───────────────┴───────────────────────────────┘

Core components

ComponentModuleDescription
FileSelectorbrowser/file_selector.pyExperiment directory tree; double-clicking opens the experiment or simulation window
ExperimentWindowbrowser/experiment_window.pyControl panel for a single experiment inside the central MDI area
ExperimentQueueexperiment/experiment_queue.pyTabular task queue
ExperimentManagerexperiment/experiment_manager.pyTakes tasks from the queue and executes them
PrepStationexperiment/prepstation.pyStaging and editing of tasks before they are sent to the queue
Looperlooper/ui.pyConditionally repeats a group of tasks based on measurement results

Details of each component are given on the corresponding page in this section.

Lifecycle

  • init_ui / create_dock_widgets: adapts the default geometry to the screen and creates the MDI canvas together with the two dock areas (the file selector on the left; the queue, preparation station and looper at the top).
  • open_experiment: opens an experiment window; if it is already open, restores it from minimised state and raises it.
  • closeEvent: first saves the main-window geometry and the state of all experiment sub-windows, then stops the queue/looper worker threads, and finally hands over to the base class to complete the close.
  • moveEvent / resizeEvent: debounced geometry saving, so that the window position and size are restored on the next start.

Usage

from quatm.GUI.browser.main_window import Browser

browser = Browser()
browser.show()

The experiment browser is the interface through which every experiment other than the helpers is run; it is normally started as the main process by the process manager.

6.2 - Experiment manager

The core experiment execution engine: takes tasks from the experiment queue and schedules them by measurement/sequence/run hierarchy.

Overview

quatm/GUI/experiment/experiment_manager.py provides the experiment manager (ExperimentManager): the engine that takes tasks from the experiment queue (ExperimentQueue) and executes them. Its queue polling runs in a worker thread, and tasks are scheduled in the hierarchy measurement → repetition → sequence/scan → run.

Execution flow

queue_fn (worker thread polls the queue continuously)
  └─→ run (task wrapper: cleanup is guaranteed however the task ends)
       ├─→ start_measurement (measurement start hook)
       ├─→ start_sequence (sequence/scan start hook)
       │     └─→ … several run calls with varying parameters …
       ├─→ end_sequence (sequence end hook)
       └─→ end_measurement (measurement end hook; clears the task table)

Key methods

MethodDescription
queue_fnContinuously polls the queue for task dictionaries: selects tasks by priority, task number and scheduled time
runTask run wrapper: finalises cleanly whether the task ends as Done/Terminated/Failed or is deleted
pausePauses the current experiment; calling it again resumes it
terminate_experimentGraceful termination: lets the current run finish naturally before stopping
start_measurement, etc.Invokes the experiment’s start/end hooks for each stage
set_dictWrites a single parameter to an attribute of the experiment instance
set_run_nr / set_rep_nrSynchronises the run/repetition index into the task dictionary and refreshes the table
due_checkDetermines whether a task has reached its scheduled execution time
start_queue / shutdownStarts / stops the resident queue worker
update_tableNotifies the GUI to refresh the displayed task table

Usage

from quatm.GUI.experiment.experiment_manager import ExperimentManager

manager = ExperimentManager(browser)   # browser supplies the queue and the UI
manager.start_queue()                  # start the resident queue worker

Tasks execute in a dedicated worker thread and do not block the GUI; when the browser closes it calls shutdown to stop the queue worker so that the process exits cleanly.

6.3 - Experiment queue

The task queue table panel: maintains priority ordering, status tracking and lifecycle management (pause/terminate/sleep/delete) of experiment tasks.

Overview

ExperimentQ (quatm/GUI/experiment/experiment_queue.py) is the “experiment queue” panel of the experiment browser (Browser) main window: a task table driven by the queue table model ScheduleModel (module quatm/GUI/widgets/models.py). Task dictionaries are stored with the task number as primary key; each task is one record containing everything needed to run it, and the table sorts by priority priority (lower value first) and then by task number.

Task status

The experiment manager (ExperimentManager) and the panel jointly maintain the status of each task:

StatusMeaning
QueuedEnqueued and waiting for execution
WaitingA due time has been set; not executed until that time is reached
SleepingAsleep; the manager ignores it for the moment (click again to wake it)
RunningA single run is in progress
ScanningA sequence/scan is in progress
PausedPaused; can be resumed
Terminating / Termination PendingGraceful termination in progress (lets the current run finish)
DoneExecution completed
FailedExecution failed

Operations and shortcuts

  • Button bar (created by create_buttons): icon buttons for pause/resume, terminate selected tasks, terminate all, and so on.
  • Context menu and single-key shortcuts (init_table_actions): e edits parameters, v opens a read-only preview, and delete, sleep/wake and pause are also available.
  • Deleting a running task: automatically becomes a graceful termination (delete_clicked).
  • Terminate (terminate_clicked / terminate_all): stops gracefully, letting the run currently in progress finish before ending.

Main methods

MethodFunction
submit(taskDict)The only entry point into the queue (runs on the GUI thread): assigns task numbers centrally; the looper (Looper) also reaches it through the submitItem signal
pause()Pauses/resumes the experiment currently running
set_sleeping()Marks the selected tasks as sleeping or wakes them
delete_item(k) / delete_clicked()Deletes by task number, or deletes the selected rows (a running task is terminated gracefully)
terminate_clicked() / terminate_all()Gracefully terminates the selected tasks / all tasks
update_item(k, v)Updates a model entry by task number and resets its lock
open_editor() / open_viewer()Edits / previews the parameters of the selected task read-only (shortcuts e / v)

Data model

# task dictionary structure (key = task number)
expDict = {
    1: {
        "expName": "Rabi scan",
        "filepath": "experiments/ion/rabi.py",
        "status": "Queued",
        "priority": 1,
        "args": {},
        "due": "",
    },
}

Sorting rule: priority (lower value first) → task number (first in, first out).

Relation to the looper

Task items of the looper (Looper) are also enqueued through ExperimentQ.submit(), so the desktop interface and loop scheduling share the same “central task-number assignment” entry channel.

6.4 - Preparation station

The staging and editing area for experiment parameters: preview, reorder and fine-tune tasks before they are pushed into the queue.

Overview

PrepStation is a tab in a top dock of the experiment browser (Browser) that acts as the “staging area” of the experiment queue: tasks that are not yet ready are kept here and are pushed into ExperimentQ for execution once confirmed. The task list is persisted in configuration/browser/prepfile.json and can be viewed and manipulated from the experiment console on the browser side (Web remote control).

Functions

OperationDescription
StageClicking the Prep button in the experiment window stages the current parameters (including the scan definition) in the task list
PushCopies the selected task into ExperimentQ (the queue retains the complete task number, priority and scan parameters)
Add to loopAdds the selected task to a Looper loop group (push_to_looper)
ReorderMoves tasks up/down to change their order
SleepMarks a task as Sleeping so that it takes no part in pushes for the time being
Edit/viewOpens the task parameter editor or a read-only view
Save/loadSaves a parameter set as JSON (experiment_params/<experiment>/<date>/)

Task data structure

Every staged task has the same structure as a queue task (generated by the experiment window’s setup_task_dict()):

task = {
    'task': 12,                 # task number
    'expName': 'Absorption',    # experiment name
    'label': 'scan: freq 2:1',  # label (includes the scan prefix)
    'priority': 0,
    'args': {'count': 5},       # experiment parameters
    'nRuns': 21,                # number of scan points
    'scanpars': ['freq'],       # scan parameters
    'scanvals': [[...]],        # scan values
    'scansequence': [...],      # scan order (after randomisation)
    'nReps': 1,
    'status': 'Queued',         # Queued / Sleeping / Waiting
    'filepath': '.../absorption.py',
}

Persistence and sharing

  • The task list is kept in configuration/browser/prepfile.json and is restored automatically when the browser restarts;
  • The desktop GUI and the Web remote control platform share the same file, so tasks staged, pushed, reordered or slept on one side are immediately visible on the other.

6.5 - Looper

The conditional loop executor: decides automatically, from measurement results, whether to repeat, jump or end an experiment loop.

Overview

Looper is a tab in the top dock of the experiment browser (Browser) (quatm/GUI/looper/). It manages groups of experiment tasks, executes the items of a group in order, and decides whether to jump or to stop based on the measurement results. Loop definitions are persisted in configuration/browser/looper/loopfile.json, and the same file is shared with the experiment console on the browser side (Web remote control).

Core logic

LoopManager.run_loop(): loop from the start group forever until terminate
  └─→ run the items of the group in order
       ├─ Task: submit to the experiment queue and advance after it finishes
       │        (max count / max consecutive count can be set; reaching either ends the loop)
       └─ Conditional: read a data-stream field → compare (> >= == != =< <)
                ├─ true → jump to the given item / end of group / end of loop
                └─ false → jump to the else item

Components

ComponentDescription
LoopGroupA loop group (one tab), containing the item list and the loop builder
LoopItemItem base class (delete/up/down/run counter/indicator LED)
TaskItemTask item: points to a staged task, supports max count/max consecutive/skip
ListItemTask list item: expands a task list and runs the tasks one by one
ConditionalItemData condition item: compares a field and then jumps (if/else)
ConditionalReachedItemReached-index condition item: jumps once the current time reaches the N-th cycle’s h5 save time
ConditionalDateTimeItemTime condition item
CallerItem / GroupItemInter-group jump and end-of-group items
DataManagerSubscribes to data streams and accumulates the latest measurement values
LoopManagerThe loop executor (run_loop() / terminate(), running in a background thread)
AutoSaveEditorConfiguration of the automatic HDF5 save schedule

How to use

Operate interactively through the Looper tab in the experiment browser (Browser):

  1. Add a group: create a new tab (New Tab #N), which can be renamed;
  2. Add items: each group can contain Task / List / Conditional / ConditionalDateTime / ConditionalReached / Caller and other items;
  3. Configure items: for Task, select the staged task number and set the maximum count; for Conditional, select the data field, comparison operator and threshold, and configure the if/else jump targets (item index / end box / end loop);
  4. Run: select the starting group and click run; LoopManager executes in a background thread and the terminate button stops it at any time.
# core classes (used internally by the GUI)
from quatm.GUI.looper import Looper, LoopManager

looper = Looper(parent=browser)
looper.loopManager.run_loop()      # start looping from the start group
looper.loopManager.terminate()     # terminate the loop

Persistence and sharing

  • Loop definitions are saved automatically to configuration/browser/looper/loopfile.json ({group name: [item dictionaries, ...]}) and restored automatically when the browser restarts;
  • The desktop GUI and the web remote-control platform share the same file, so changes made on either side are immediately visible on the other; the browser side likewise supports group management, Task/Conditional item editing and starting/stopping loops.

6.6 - Experiment window

The per-experiment control window: dynamic parameter grid, submission/scanning and parameter persistence.

Overview

quatm/GUI/browser/experiment_window.py provides the experiment window (ExperimentWindow): each experiment script corresponds to a control window carried by an ExperimentSubWindow in the browser’s MDI area. It generates a parameter grid automatically from the parameter objects declared by the experiment, and is responsible for submitting to the queue/preparer, building scans and loading/saving JSON parameters.

Components

ComponentDescription
ExperimentSubWindowGraphical container: stores the window geometry and clamps the window inside the visible area
ExperimentWindowSingle-experiment control panel: parameter grid + submit/scan/save controls
SequenceEditorScan sequence dialog: sets start, stop and step count per dimension to generate an nD scan

Key methods (ExperimentWindow)

MethodDescription
submit_to_queueSubmits the current experiment to the experiment queue for execution (the unified entry point for all submission buttons)
submit_nextSubmits with the highest priority, ahead of all existing tasks
submit_to_prepperSubmits to the preparation station (PrepStation) for staging
setup_scanBuilds the scan sequence (parameter values and randomised run order) from the scan definition
setup_task_dictBuilds the task dictionary stored in the queue/preparation station
build_argument_dictCollects values from the parameter grid controls into a complete parameter dictionary
save_params / load_paramsSaves parameters to JSON (named by date/time, optionally backed up) and loads them back
adaptive_columnsRe-flows the parameter grid columns to the window width (roughly one extra column per 200 px)
edit_sequenceOpens the scan sequence editor and applies the new sequence

Usage

from quatm.GUI.browser.experiment_window import ExperimentWindow

# normally opened by Browser.open_experiment; may also be constructed directly
win = ExperimentWindow(filepath=..., props=props, parent=subwindow, browser=browser)

The parameter objects declared by the experiment script (such as NumberValue / BoolValue / StringCombo) automatically produce editing controls with a label, unit and range; parameter sets can be saved as json and loaded again later.

6.7 - Parameter system

The BasicManager parameter-manager system: maps experiment properties to numeric, boolean, combo and frequency controls.

Overview

quatm/GUI/entries/dashboard.py provides a family of “parameter manager” controls: each manager maps one experiment parameter (stored in the property database, always in SI units internally) to a labelled input control, and synchronises bidirectionally between the interface and the property database.

Control class hierarchy

BasicManager (base class of numeric parameter entries)
├── IntManager        → integer spin box
├── FloatManager      → float spin box (optional unit/step/decimals)
├── BoolManager       → check box
├── ComboManager      → drop-down box (string list)
└── FrequencyManager  → DDS frequency spin box (stores Hz, shows MHz)

Usage (schematic)

from quatm.GUI.entries.dashboard import IntManager, FloatManager, BoolManager

# each manager binds the property store props and the experiment exp, reading/writing by parameter name
mgr = IntManager(props=props, exp=exp, parname="my_int")
fmgr = FloatManager(props=props, exp=exp, parName="cooling_time", unit="µs")
bmgr = BoolManager(props=props, exp=exp, parName="enable")

Changes made in the interface are converted to SI and written back to the property database; changes in the property database are likewise synchronised to the displayed values periodically by each control (updateValue / updateSpin).

See also “Experiment parameter panel”: on this basis the Dashboard main panel arranges the parameters of a whole device into an adjustable control panel.

6.8 - Property editor

A ParameterTree-based tool for viewing and editing properties.

Overview

quatm/GUI/entries/property_editor.py provides a property editing tool based on parametertree: it presents the key/value tree of the property database as an expandable, editable parameter tree with search filtering, and writes changes back immediately.

Components

ComponentDescription
TreeEditExtracts information from Properties and arranges it into a tree-view structure
PropEditMain property editing window (ParameterTree): subtree browsing, search, saving/deleting the selected node

Usage

from quatm.GUI.entries.property_editor import PropEdit

editor = PropEdit(name="device_status", props=props, subtree="/", parent=parent)

Note: subtree restricts the view to the properties under a given path; TreeEdit is responsible for converting the property dictionary into a tree of Parameter nodes.

6.9 - Live plotting

A live plot item that subscribes to data streams and keeps updating, together with its widget container.

Overview

quatm/GUI/entries/updating_plot.py provides live plotting: UpdatingPlot is a PlotItem subclass that subscribes to a data stream, periodically pulls new data and refreshes the curve; LivePlot is a widget container that embeds one UpdatingPlot, which makes it easy to dock into other windows as a panel.

Components

ComponentDescription
UpdatingPlotLive plot item: subscribes to a data stream and keeps the curve updated
LivePlotWidget container: embeds one UpdatingPlot and may carry an image stream name

Key methods (UpdatingPlot)

MethodDescription
setnewDataPeriodically pulls new data; a one-dimensional array gets an x axis automatically, then the curve is updated according to the rotation flag
updateConfigurationCancels the old subscriptions, then resubscribes and rebuilds the curves from the stream list configured in the properties
subscribe_windowOpens the subscription editor to select data streams and reapplies the subscription configuration on close
configureWindowOpens the property editor dialog to adjust settings and reapply the display configuration

Usage

from quatm.GUI.entries.updating_plot import UpdatingPlot, LivePlot

plot = UpdatingPlot(name="PMT signal")        # use directly as a plot item
host = LivePlot(name="PMT signal", parent=parent)  # or embed as a container in the UI

Subscriptions are made in the context menu of the plot item; LivePlot is also used as the container for the row/column profile plots in the image viewer.

6.10 - LED indicator

A status LED: green/red/cyan color schemes, radial gradients and lit/unlit appearances.

Overview

LedIndicator (quatm/GUI/widgets/qled.py) is an LED indicator button whose lit/unlit appearance can be toggled: a two-layer circular shell with a gradient centre, using highlighted colors when checked (lit) and dark colors when unchecked (dark). It is commonly used to show device connection status, experiment run status and the execution feedback of loop items.

Color modes

MethodDescription
set_green()Green scheme: bright green when checked, dark green when unchecked; usually indicates normal/ready
set_red()Red scheme: bright red when checked, dark red when unchecked; usually indicates an error/warning
set_cyan()Cyan scheme: used by looper condition items while they wait for data

The lit/unlit (checked) state is controlled with setChecked(True/False); the onColor1/onColor2 and offColor1/offColor2 properties customise the primary/secondary colors of the on and off states.

Usage

from quatm.GUI.widgets.qled import LedIndicator

led = LedIndicator(parent=parent)
led.set_green()      # device ready
led.setChecked(True) # switch on
# ...
led.set_red()        # device fault

6.11 - GUI base classes

BWidget, BMainWindow and BFrame: Qt base classes with geometry persistence and property communication.

Overview

quatm/GUI/widgets/quatmQt.py defines the base classes and common controls of the Quatm GUI components: it provides window geometry persistence, the Properties property namespace, and a combo box with completion.

Class hierarchy

QWidget  → BWidget
QFrame   → BFrame
QMainWindow → BMainWindow

Base classes and mixins

ComponentDescription
GeometrySaveMixinPersists window geometry to QSettings (keyed by window name); saves on close, move and resize (debounced)
BWidgetQuatm widget base class: carries the Properties namespace and geometry persistence
BFrameBase class corresponding to QFrame, with the same behaviour as BWidget
BMainWindowMain-window base class with geometry persistence and a property handle
from quatm.GUI.widgets.quatmQt import BWidget, BMainWindow

widget = BWidget(name="my_widget", parent=None)
window = BMainWindow(name="MainWindow", parent=None)

Search and completion controls

ComponentDescription
CustomCompleterCompleter with match modes (contains = substring anywhere / begins = prefix)
SearchComboBoxCombo box with autocompletion and type-ahead search; add_if_new() / find_or_add() maintain entries conveniently
SearchLineEditLine edit for search boxes: selects all on focus, records on focus-out/Enter, restores on Escape
from quatm.GUI.widgets.quatmQt import SearchComboBox

box = SearchComboBox(parent=parent)
box.add_if_new("my_stream")

6.12 - Image monitor

A self-updating image display control: image/mask stream subscription, denoising and spot detection, level and ROI configuration.

Overview

quatm/GUI/entries/image_monitor.py provides the automatically updating image display window (ImageDisplay): it periodically pulls the latest frame from the image stream subscribed in the properties, transforms it by resolution and offset and refreshes the display, optionally overlaying a mask stream in sync. It has built-in denoising, spot detection, saving of the current frame and property configuration (display levels, ROI, etc.).

Methods

MethodDescription
update_imagePeriodically pulls the image stream and refreshes the display frame by frame after resolution/offset transforms
update_maskPulls the mask stream and updates the overlay geometry from the mask resolution/offset
noise_filterDenoises with a bilateral filter plus a median filter according to the property parameters
spot_detectorBinarises the image by threshold to detect bright spots
saveCurrentImageSaves the currently displayed frame as PNG (named by time)
subscribe_windowOpens the subscription dialog to select image streams and resubscribes
subscribe_maskOpens the subscription dialog to select mask streams and resubscribes
configureWindowOpens the property editing dialog to adjust display levels, ROI and other parameters

Usage

from quatm.GUI.entries.image_monitor import ImageDisplay

view = ImageDisplay(imagestreams=["camera/image"], parent=parent)
view.configureWindow()   # adjust display levels, ROI, etc.

Note: ImageDisplay is both the central display of ImageWindow and can be embedded on its own in other interfaces. The image and mask stream lists are stored in the properties, and the context menu provides entries for subscription, mask subscription and configuration.

6.13 - Analysis manager

A tree-view panel: manages the start, stop, status monitoring and automatic restart of analysis scripts.

Overview

quatm/GUI/entries/analysismanager.py provides the analysis manager: it lists the analysis scripts in a checkable tree table (name / script / category / input stream) and is responsible for starting and stopping subprocesses, monitoring and automatic restart, as well as adding, deleting and configuring analysis entries.

Components

ComponentDescription
AnalysisManagerMain panel: loads and organises the analysis entries, and provides delete/configure entry points
TreeViewWidgetAnalysis script management tree: checking starts and stops, monitoring restarts automatically, add/delete/edit
addAnalysisWidgetToolbar for adding an analysis entry (name / category / script / input stream)

Functions

OperationDescription
Check to startChecking an entry starts the corresponding analysis subprocess; unchecking stops it
StopendProcess: terminates first and waits briefly for exit, then forces the end on timeout
Status refreshupdateStatus: periodically checks whether the process is still running
Clean up stray processeskillVagabonding: terminates leftover processes of the same script from the previous round
Add / configure / deleteThe toolbar registers new entries; a selected row can be configured or deleted

Usage

from quatm.GUI.entries.analysismanager import AnalysisManager

mgr = AnalysisManager(name="AnalysisManager", parent=parent)

Analysis scripts are independent processes: once an entry is checked, the manager launches the script with the current interpreter and injects the project environment variables into the child process, so analysis code and the main program are isolated from each other and a script crash cannot affect experiments.

6.14 - Stream monitor

Stream monitoring: multi-tab data/image/command/message monitoring and single-stream panels.

Overview

quatm/GUI/streammonitor/ provides the stream monitor: MainWindow uses tabs to hold monitors for data streams (Data), image streams (Image), commands (Command), message logs (Message) and history (History), among others; each monitor tab is a StreamMonitor that subscribes to a single data stream and displays it in real time.

Components

ComponentDescription
MainWindowMonitoring main window: multi-tab container + menu
StreamMonitorSingle-stream monitoring panel: subscribes to one data stream and displays it in real time
MessageTableModelMessage log table model (timestamp/source/level/content, colored by level)
ImageTableModelImage stream metadata table (fixed time/source columns + dynamic key columns)
AutoSortProxyModelAuto-sorting proxy model ordered by descending timestamp

Monitoring types

TabContent
DataReal-time curves/tables of numeric data streams
ImageScientific image streams and their metadata
CommandCommand / instruction streams
MessageMessage log (timestamp/source/level/content)
HistoryHistorical records

Usage

from quatm.GUI.streammonitor import MainWindow, StreamMonitor

win = MainWindow(parent=parent)   # multi-tab monitoring main window
panel = StreamMonitor(name="PMT", stream_type="Data", parent=parent)  # single-stream panel

This monitoring interface is normally started as a standalone process; the single-stream StreamMonitor panel can also be embedded in other windows.

6.15 - Sequence editor

Experiment code editing and sequence simulation: syntax highlighting, parsing and output-channel timing visualisation.

Overview

quatm/GUI/editors/editor_sequencer.py provides the edit and simulate tool (formerly Combi / “open simulation window”): the CodeEditorParser composite main window combines the CodeEditor on the left with the sequence visualisation on the right: after editing an experiment script you can simulate a run with a single click, drawing the timing of each output channel as a step plot, which makes experiment sequences easier to debug.

Components

ComponentDescription
CodeEditorSimple code editor: line numbers, Python highlighting, automatic indentation and completion
PythonHighlighterLightweight Python syntax highlighter
LineNumberAreaLine-number margin widget on the left of the editor
ExperimentParserParses an experiment: buildExperiment / simulateExperiment return the command stack
ExperimentSequencerDraws a sequence as a step plot of each output channel against time
CodeEditorParserComposite main window: Save / Simulate / Clear toolbar + dock layout

Key methods

MethodDescription
saveFile (CodeEditor)Saves the current text to a file (returns True on success)
simulateExperiment (CodeEditorParser)Simulates the current version of the experiment and hands the command stack to the sequence visualisation
buildExperiment / simulateExperiment (ExperimentParser)Builds / simulates the experiment and returns the command stack
plotSequence (ExperimentSequencer)Draws the sequence dictionary as a per-channel timing step plot

Usage

from quatm.GUI.editors.editor_sequencer import CodeEditorParser

win = CodeEditorParser(parent=parent, filename="experiments/my_experiment.py")

Note: parameter scans (nD scans, randomisation, etc.) are edited through the Edit button of the experiment window (SequenceEditor, see Experiment window); the scan definition is stored in the experiment property tree and shared with the remote-control platform.

6.16 - Process manager

A distributed process panel: starts and stops background processes grouped as Servers/Driver/GUI/Viewer/Monitor.

Overview

quatm/GUI/entries/processmanager.py provides the process manager: it reads the configuration by category (Servers / Driver / GUI / Viewer / Monitor) and generates a SingleProcess start/stop button panel for each script entry, so the whole experiment software suite is managed from one place. Processes are launched with the current interpreter, either as a script or as a module (python -m), carrying the project root and the parent-process watchdog environment.

Process categories

CategoryDescriptionTypical processes
ServersCommunication / property / data servicesData centre, property service, etc.
DriverHardware driver processesFPGA and other device drivers
GUIGraphical interface processesExperiment browser, data manager, etc.
ViewerData viewing processesLive plotting, scroll window, image viewer, etc.
MonitorMonitoring / checking processesProperty checker, message monitor, etc.

Components

ComponentDescription
ProcessManagerCategory panel: reads the configuration and generates start/stop buttons for each entry
SingleProcessSingle-entry panel: start / asynchronous termination / status polling with coloring
resolve_scriptResolves the script entry from the configuration into a launch target (file path or module)

Key methods (SingleProcess)

MethodDescription
startProcessStops the old instance and then starts the subprocess with the current interpreter
stopProcessTerminates the subprocess asynchronously without blocking the GUI thread
updateStatusPeriodically polls the exit status and colors it (running / exited normally / crashed)

Usage

from quatm.GUI.entries.processmanager import ProcessManager

pm = ProcessManager(parent=parent)
pm.show()

The entries to manage are defined by the scripts in the configuration file; each entry is started and stopped through its own button panel.

6.17 - Device manager

PyVISA device discovery and driver binding: enumerate, query and select instruments and associate driver files.

Overview

quatm/GUI/entries/device_manager.py provides the device manager (VISADeviceLister): it scans the VISA instruments on the system through PyVISA, queries the device identity (*IDN?) and selects a driver file to bind, then adds the device to the selected list as a DeviceInfo record for use in device configuration.

Components

ComponentDescription
VISADeviceListerVISA device list tool: two columns (enumeration/query + selected devices/driver files)
DeviceInfoDevice description: name, type, driver file and additional parameters

Workflow

1. refresh_devices()      → scan the VISA bus and fill the device list
2. query_device_info()    → send an *IDN? query to the selected device
3. select_driver_file()   → choose and bind a driver file
4. add_device_to_selected() → add to the selected device list (de-duplicated)

Usage

from quatm.GUI.entries.device_manager import VISADeviceLister

dl = VISADeviceLister(parent=parent)
dl.refresh_devices()   # list the connected USB/GPIB/LAN and other VISA devices

6.18 - Data storage

HDF5 data management: subscription, scheduled/manual saving and history read-back, with integrated plotting panels.

Overview

quatm/GUI/h5storage/ provides the HDF5 data storage GUI (a package split out of the former single file, with its public symbols re-exported at package level): three kinds of data manager — aggregate, single-stream and image — subscribe to data and save it automatically at a scheduled time, periodically, or on a Looper command; the current snapshot can also be written to an .h5 file in the background, and historical measurements read back from a file.

Components

ComponentModuleDescription
H5StorageGuimain_window.pyMain window: data tabs, save/scheduled-save controls and plot management
H5StorageDataMgrdata_manager.pyHost for the three data managers: subscription management, save/read-back, clearing
H5SaveWorkerworkers.pyWorker thread that writes the .h5 file, keeping disk I/O off the Qt event loop
H5DataPlotter / EventPlotterplotters.pyPlotting tabs for measurement data / event data
XAxisWidget / YAxisWidgetaxes.pyData selection and filter-chain configuration for the x axis and each y axis
BinningWidget / FilterWidget / FittingWidgetwidgets.py / fitting.pybinning, filtering and ODR fitting processing chain

Saving modes (H5StorageDataMgr)

MethodDescription
toggle_scheduled_saveToggles saving at a scheduled time (saves automatically and notifies when the time arrives)
toggle_regular_saveToggles periodic automatic saving every N hours
scheduled_save_by_looperResponds to save commands from the Looper (final / intermittent)
savetoFileWrites the current data snapshot to an .h5 file in the background
loadFromFileLoads historical measurements from an existing .h5 file and refreshes the interface
selectDatadirChooses the target h5 file manually (to avoid falling back to the daily default name)
clearClears the entire cache of the three data managers

Usage

from quatm.GUI.h5storage import H5StorageGui   # package-level re-export

win = H5StorageGui(parent=parent)
win.show()

Data saving is performed by the background H5SaveWorker: the GUI thread copies the data snapshot first and then hands it to the worker, so writing to HDF5 on disk never blocks the interface.

6.19 - Live scroll plot

Live scrolling plots: ring buffers, running averages and data-stream subscription.

Overview

quatm/GUI/entries/live_plot.py provides live scrolling plots: ScrollWindow is a multi-subplot main window in which each ScrollPlot subplot subscribes to several channels of one data stream, maintains a ring buffer and scrolls scatter/curve data and a running average; PlotDataEditor provides the data-source drop-down panel.

Components

ComponentDescription
ScrollWindowMulti-subplot scrolling main window: restores or creates subplots from the properties; the toolbar can append subplots
ScrollPlotScrolling subplot: ring buffer, scatter and running-average curves, context-menu operations
PlotDataEditorData-source selection panel: drop-down candidates written back to the properties/parent plot

Key methods (ScrollPlot)

MethodDescription
feedDataPushes a new data block into the buffer array
setnewDataPeriodically pulls new data into the buffer and refreshes the curves
updatePlotFlushes the valid data in the buffer to the scatter and running-average curves
running_meanComputes an N-point running average efficiently from cumulative sums and differences
dataSelectDialogSelects the data channels displayed in this plot
subscribe_windowOpens the subscription editor to resubscribe the data stream
configureWindowPauses refreshing and edits this plot’s properties inline
deletePlotRemoves this plot from the parent window and clears its properties

Usage

from quatm.GUI.entries.live_plot import ScrollWindow

host = ScrollWindow(name="PMT scroll monitor", parent=parent)
host.addPlot()   # append a scrolling subplot

Afterwards, subscribe to data streams, select channels and adjust the display from the context menu of a subplot. Configurations such as the buffer length, the running-average switch and the window size are stored in the properties and restored automatically after a restart.

6.20 - Auto-save editor

Editor for the list of automatic save times: configures a set of periodic save instants (checkbox + date-time).

Overview

AutoSaveEditor (quatm/GUI/editors/auto_save_editor.py) is a pop-up editor used to maintain the “time list” of periodic h5 automatic saves: each row = one (checkbox + date-time), and a checked row triggers one automatic save at the corresponding time. It is invoked by the data-storage panels.

Components

ComponentDescription
AutoSaveEditorDialog for editing the list: loads/writes back the properties, adds, deletes and edits rows, and confirms (Accept)
DateTimeWidgetEditing row for a single automatic save time: index + checkbox + date-time picker

Main methods

MethodFunction
load_AutoSaveEditor_props(props, prop_name)Reads the saved date-times and check marks from the properties, padding them up to the maximum number of items
update_datetime(idx, datetime_save)Saves a new date-time for a row
update_ticked(idx, ticked)Saves the checked state of a row
accept()Confirms: writes the list back to the properties and closes

Usage

from quatm.GUI.editors.auto_save_editor import AutoSaveEditor

editor = AutoSaveEditor(props=props, max_items=24, parent=parent)
editor.load_AutoSaveEditor_props(props, "H5Storage/auto_save_times")

6.21 - Experiment helpers

Optical-tweezer alignment and stray-field compensation: a toolkit for automated calibration and optimisation.

Overview

Quatm wraps routine experiment calibration tasks as alignment/helper tools. The modules live in quatm/GUI/alignment/, together with a number of standalone entry-point widgets, and provide one-click runs and automatic optimisation.


ODT alignment helper — OdtAlignmentHelper

quatm/GUI/alignment/odt_helper.py — builds a single-axis control block for each piezo mirror axis of xODT/cODT, and provides overall run/terminate together with compensation voltage and BA laser parameter settings. The complete alignment sequence for each axis is: full-range scan with the laser off → fine scan near the ion → publish the fit result → move to the target position.

from quatm.GUI.alignment.odt_helper import OdtAlignmentHelper

helper = OdtAlignmentHelper(parent=parent)
helper.run_all()   # run the full alignment sequence (thread pool keeps axes sequential)
helper.terminate_all()   # safely stop all axes

Stray-field compensation — CompensationHelper

quatm/GUI/alignment/comp_helper.py — obtains the compensation voltage from a voltage scan along each trap axis as the intersection of two fitted lines, minimising the stray field automatically.

ComponentDescription
PlotWidgetPlot widget with one tab per trap axis
ControlWidgetControl area for each axis plus a shared parameter area
SingleAxisWidgetScan parameters and “find compensation voltage” (intersection of the two fit lines)
CompVoltageScanPerforms the voltage scan at the high and low rf confinement
from quatm.GUI.alignment.comp_helper import CompensationHelper

comp = CompensationHelper(parent=parent)

Piezo mirror control — PiezoMirrorControl

quatm/GUI/entries/piezo_mirror_controler.py — a simple panel that steers the direction of the mirror driven by a linear actuator using arrow keys/buttons; W/A/S/D correspond to up/left/down/right.

from quatm.GUI.entries.piezo_mirror_controler import PiezoMirrorControl

mirror = PiezoMirrorControl()   # standalone window; control with WASD or the arrow keys
mirror.show()

6.22 - Data models

DictSyncModel/ListSyncModel: maps Python dictionaries/lists onto Qt tables automatically.

Overview

quatm/GUI/widgets/models.py provides the base classes that map Python dictionaries / lists onto Qt table models (derived from ARTIQ’s models). The table interfaces of the experiment queue, preparation station and looper are all built on them.

Core classes

ClassDescription
_SyncSubstructHelper wrapping dictionary operations on the underlying container: triggers the refresh callback after append/insert/pop
DictSyncModelDictionary→table model base class: maps backing_store through the headers/dataNames columns
ListSyncModelList→table model base class (the backing store is a list)
ScheduleModelModel for the experiment queue (ExperimentQueue): tasks sorted by priority and then by task number
PrepModelModel for the preparation station / looper: the backing store is a list whose order can be changed freely

Usage

from quatm.GUI.widgets.models import DictSyncModel

model = DictSyncModel(
    headers=["Name", "Value", "Unit"],
    dataNames=["name", "value", "unit"],
    init={"name": "param1", "value": 100, "unit": "µs"},
)

Concrete business models customise their behaviour by implementing three hooks in a subclass: convert turns a key/value/column into cell content, sort_key returns the sort key, and flags decides per-cell flags such as whether a cell is editable.

6.23 - Collapsible components

CollapsibleDialog: a collapsible/expandable tree dialog.

Overview

quatm/GUI/widgets/theexpandables.py provides the collapsible/expandable tree dialog CollapsibleDialog: each “section” consists of a tree item with an expand/collapse button and a content widget, which suits configuration interfaces and advanced parameter settings.

Components

ComponentDescription
SectionExpandButtonButton that expands/collapses a section
CollapsibleDialogDialog container; subclasses override define_sections() to define all panels

CollapsibleDialog provides add_section(title, widget) to add a panel directly; subclasses may also override define_sections() to organise the tree structure in detail with add_button / add_widget.

Usage

from quatm.GUI.widgets.theexpandables import CollapsibleDialog

class SetupDialog(CollapsibleDialog):
    def define_sections(self):
        self.add_section("Camera settings", some_widget)
        self.add_section("Trigger settings", trigger_widget)

6.24 - Parameter boxes

Input controls bound to experiment parameters: float, boolean and combo parameter boxes that write changes back immediately.

Overview

quatm/GUI/widgets/arg_boxes.py provides widgets that bind experiment parameter classes to Qt input controls: when the user changes a value it is written back to the experiment parameter (stored internally in SI units, converted for display as needed). They are usually created automatically by the “experiment parameter panel / parameter system” according to the parameter type.

Controls

ControlParameter typeDescription
FloatBoxNumeric parameterFloating-point input; converts units via unit and display_multiplier before writing back
BoolBoxBoolean parameterCheckbox; the value is written back as a boolean
ComboBoxList of stringsCombo box; takes the value from the string list by the selected index

Usage (schematic)

from quatm.GUI.widgets.arg_boxes import FloatBox, BoolBox, ComboBox

# numeric parameter: shown in kHz in the UI, stored in SI (Hz) underneath
fbox = FloatBox(argument=param_freq, unit="kHz", display_multiplier=1e3)

bcheck = BoolBox(parName="enable", value=param_enable)
cbox = ComboBox(parName="source", argument=param_source)  # the parameter class holds a stringlist

These controls are normally generated automatically by the parameter system from the experiment’s declarations, so there is no need to instantiate them by hand.

6.25 - Experiment parameter panel

DefaultExp + Dashboard: a whole-device parameter control panel built automatically from the device database.

Overview

The Dashboard (quatm/GUI/entries/dashboard.py) is the “control panel” main widget driven by the default experiment (DefaultExp): DefaultExp assembles the parameters of the whole machine from the device database automatically, and Dashboard then arranges these parameters in sections by integer/float/boolean and as a grouped grid, forming an adjustable parameter control interface.

Class hierarchy

DefaultExp (experiment base class: auto-assembles device properties and groups them)
     └─ Dashboard (control panel: sections + grouped grid + adaptive column count)
            └─ manager widgets (IntManager / FloatManager / BoolManager / ComboManager …)

Key features

  • Automatic assembly: DefaultExp.build() walks the device library and creates a parameter object for every attr/generic_attr device, classifying it by its group.
  • Sections by type: the interface is split into integer / float / boolean sections, and the parameters are grouped within each section.
  • Adaptive column count: adaptive_columns() reflows according to the window width (starting at 4 columns, adding one column per ~200 px), so that narrow windows are not squeezed.
  • Immediate effect: changing a spin box writes back to the properties and straight through to the hardware (the run body run() is empty, which suits purely manual tuning).

Usage (schematic)

from quatm.GUI.entries.dashboard import Dashboard, DefaultExp

# DefaultExp auto-assembles the device parameters; Dashboard handles layout and grouping
page = Dashboard(parent=parent)

The “Parameter system” page describes the manager control classes behind it.

6.26 - FPGA DAC control

A simple manual control interface for the FPGA: 16 DAC voltages and 32 TTL bitmask settings.

Overview

quatm/GUI/entries/fpga_controller.py provides simple manual FPGA control for debugging and calibration: each DAC voltage is set with a labelled -10 … +10 V spin box, and the 32 TTL outputs are set with a grid of checkboxes.

Components

ComponentDescription
dacv(v)Converts -10..+10 V into the 16-bit raw DAC code used by the FPGA
DACsingleA single DAC channel: a labelled -10..+10 V spin box, effective as soon as it is written
DACvaluesAll 16 DAC channels stacked vertically
TTLQWThe 32 TTL outputs: an 8×4 grid of checkboxes, written as a single 32-bit mask
simple_pageThe DAC + TTL manual control page
centralWidget / mainQMWTab container and simple main window (with a File/quit menu)

Usage

from quatm.GUI.entries.fpga_controller import DACvalues, TTLQW

# obtain the low-level FPGA driver handle (controller) from the device database
page = DACvalues(controller)

# TTL: checkbox states are combined into a mask by the "Apply/state change" callback and written to the FPGA
ttl = TTLQW(controller)

If the hardware is missing or a write fails, it only prints an error instead of crashing, which makes the interface easy to debug without hardware.

6.27 - Background worker threads

Worker/WorkerSignals: a generic QRunnable background-task wrapper.

Overview

quatm/GUI/widgets/browser_workers.py and helper_workers.py provide a generic background-thread wrapper based on QRunnable: time-consuming tasks are executed in a thread pool, and results/exceptions/progress are passed back to the GUI thread through signals.

Signals

SignalDescription
finishedTask completed (no data)
error(tuple)(exception type, value, traceback string)
result(object)The result returned by processing, of any type
progress(int)Progress percentage

Usage

from quatm.GUI.widgets.browser_workers import Worker

worker = Worker(callback=my_long_task, arg1, kw=...)   # passed through to the callback
worker.signals.finished.connect(on_done)
worker.signals.error.connect(on_error)
QThreadPool.globalInstance().start(worker)

The two implementations are largely the same: helper_workers.py is reused for helper/analysis background tasks, while browser_workers.py is reused for browser-style panels.

6.28 - Experiment timeline

FPGA experiment timing visualisation: a chart viewer for DAC and TTL output channels against time.

Overview

quatm/GUI/entries/experiment_timeline.py provides the experiment timeline (Timeline): it loads an experiment timing JSON file (experiment.json), draws the variation of each DAC/TTL output channel over time as step subplots, and provides a checkbox panel of output channels to decide which curves are visible, which makes experiment timing logic easier to debug and verify.

Components

ComponentDescription
TimelineTimeline viewer main window: file loading, plotting and channel control
TimelinePlotterTiming visualisation widget: plots the state of each active output against time
SelectorWidgetOutput-channel checkbox panel: select all / select none decides the visible curves

Key methods (Timeline)

MethodDescription
load_fileLoads and parses the timing JSON, constructs the time series and refreshes the channel selector
plot_experimental_sequenceCreates a separate subplot for each output according to the channel selection and draws a step plot
decode_ttl_bitmaskSplits the 32-bit TTL bitmask into set/clear commands for the individual channels
construct_time_sequencesConverts the step-wise timing into times/vals sequences organised per channel
reverse_device_db_transformationDevice-database reverse transformation: maps internal names back to readable names (including the gauge calibration table)

Usage

from quatm.GUI.entries.experiment_timeline import Timeline

tl = Timeline()
tl.load_file("experiment.json")   # load a timeline file and plot it
tl.show()

6.29 - Scroll window

A multi-channel real-time scrolling data viewer window: subplot buffers, data-stream subscription and dynamic refreshing.

Overview

quatm/GUI/entries/scrollwindow.py provides the scroll window (ScrollWindow): a BMainWindow subclass that acts as a multi-channel real-time scrolling data viewer main window. It lays out ScrollPlot subplots in the requested number (two per row), distributes data blocks to the corresponding subplots and refreshes them uniformly; PlotDataEditor provides the data/image stream selection drop-down.

Components

ComponentDescription
ScrollWindowMain window: creates subplots by count, distributes data and refreshes
ScrollPlotScrolling subplot: subscribes to a data stream, ring buffer + periodic refresh
PlotDataEditorData-source selection panel: drop-down candidates written back to the property ydata

Key methods

MethodDescription
initPlotsCreates the scrolling subplots by count and places them in the layout (two per row)
feedDataFeeds a new data block into the buffer data array
updatePlotBatch-refreshes the filled interval of the buffer into the curves of each subplot
setnewData (ScrollPlot)Timer-driven data pull and curve refresh
dataSelectDialog (ScrollPlot)Opens a dialog to select the data channels to plot
subscribe_window (ScrollPlot)Opens the subscription editor to change the data stream
configureWindow (ScrollPlot)Pauses refreshing and edits this plot’s configuration inline

Usage

from quatm.GUI.entries.scrollwindow import ScrollWindow

win = ScrollWindow(name="PMT scroll monitor", parent=parent, n_plots=2)

Data-stream subscription, data-channel selection and display configuration are performed on each subplot through its context menu; the window can also be started as a standalone process.

6.30 - Subscription editor

Manages the subscription list of data/image streams: adds, deletes and hot-updates subscribed sources.

Overview

SubscriptionEditor (quatm/GUI/editors/subscription_editor.py) manages the program’s subscriptions to data streams/image streams: it loads the stored subscriptions from the property database, lists the available streams for selection, supports adding/deleting, and hot-updates the list when properties change. It is commonly used as the data-source selection component of live plotting/monitoring panels.

Main methods

MethodFunction
loadSubscriptions()Reads the saved subscriptions from the property database and loads them into the list
update_streamlist()Rebuilds the drop-down candidates from the currently available data streams
add()Adds the stream selected in the combo box to the subscription list and synchronises the properties
deletemarked()Removes the subscription items marked for deletion and synchronises the properties
handle_property_changes(changes)Listens for property changes, refreshes the list and broadcasts the change

Usage

from quatm.GUI.editors.subscription_editor import SubscriptionEditor

editor = SubscriptionEditor(props=props, category="Data", parent=parent)
editor.loadSubscriptions()

6.31 - Table parameter

A QTableWidget-based editor for numpy structured arrays, used in parameter trees.

Overview

quatm/GUI/widgets/table_parameter.py provides a table editing node for ParameterTree: the table_parameter parameter type works together with the table_parameterItem widget item to edit numpy structured arrays (such as calibration tables and scan sequences) directly in the parameter tree.

Components

ComponentDescription
table_parameterParameter type: accepts a numpy structured array as its value
table_parameterItemCorresponding widget item: creates a QTableWidget as the editor from the array dtype

table_parameterItem creates columns from the array dtype (including field name/type), and cell edits are written back to the array immediately; valueIsDefault() is used to decide whether the value needs to be persisted separately.

Usage

from quatm.GUI.widgets.table_parameter import table_parameter
import numpy as np

dt = np.dtype([("freq", float), ("amp", float)])
param = table_parameter(name="scan_table", value=np.zeros(10, dtype=dt))

6.32 - Remote ROI

An ROI component that publishes color/position/size through the central property database and can be synchronised remotely.

Overview

quatm/GUI/widgets/ROI.py provides ROI: it inherits ROI and communicates its color, position and size through the central property database under a unique name, so that remote clients can view and adjust the ROI region in sync.

Methods

MethodDescription
update_from_propertiesRefreshes the ROI pen color and geometry from the color/position/size stored in the properties

Usage

from quatm.GUI.widgets.ROI import ROI

roi = ROI(name="analysis_roi", parent=view)   # name must be unique; duplicate names share properties
roi.update_from_properties()

Note: name is the key of this ROI in the central property database, so make sure it does not clash with any name in the property dictionary; after a property change, calling update_from_properties refreshes the ROI appearance and position in sync.

6.33 - Image viewer

The scientific image viewing main window: a central image display with row/column profile and histogram LUT dock panels.

Overview

quatm/GUI/entries/image_group.py provides the image viewing window (ImageWindow): the central ImageDisplay is the main widget, with panels such as the row/column profiles and the histogram LUT docked around it, for real-time monitoring and analysis of imaging experiments.

Layout

Dock areaWidgetPurpose
CentralImageDisplaySubscribes to and automatically refreshes an image stream; can overlay a mask
TopLivePlot (row profile)Intensity profile of an image row
RightLivePlot (column profile)Intensity profile of an image column
LeftHistogramLUTWidgetHistogram color scale: level range and false-color mapping

Each profile plot is a LivePlot container in which an UpdatingPlot subscribes to the corresponding data stream and refreshes in real time (see Live plotting for details).

Display parameter persistence

Display parameters are stored in the Viewer/ namespace of the property database; they are restored from the properties at construction time and saved automatically on close:

PropertyMeaning
Viewer/minlevels / Viewer/maxlevelsHistogram display level range
Viewer/gradientFalse-color gradient preset (such as magma)
Viewer/tick_positionColor scale tick positions (0–1 normalised list)

After dragging the histogram levels or the color scale ticks, the current values are written back to the properties through update_min_max and get_tick_position respectively, and closeEvent saves the view range and ticks uniformly before the window closes.

Methods

MethodDescription
update_min_maxWrites the current histogram level range back to the properties (min/max levels)
get_tick_positionWalks the color scale ticks and returns them normalised to the 0–1 range
closeEventSaves the view range and color scale ticks to the properties before closing

Usage

from quatm.GUI.entries.image_group import ImageWindow

win = ImageWindow(name="absorption_imaging")   # standalone top-level window; parent may be omitted
win.show()

This window is often started as a standalone process, but can also be embedded in other interfaces as a BMainWindow instance. The image stream and the row/column profile streams are subscribed from the context menus of the individual sub-panels (image display / profile plots).

6.34 - Property checker

A property-distribution watchdog: verifies that the Properties hub is really broadcasting updates.

Overview

PropertiesChecker (quatm/GUI/entries/properties_checker.py) is a property-distribution watchdog: it sets a marker through one property handle and reads it back through a second handle; if the hub has not propagated the change, it raises an alert indicating that the property service may have stalled. The check runs periodically on a timer, so the window is displayed normally and the GUI event loop stays responsive.

Usage

from quatm.GUI.entries.properties_checker import PropertiesChecker

checker = PropertiesChecker(parent=parent)
checker.check_once()   # run one watchdog check manually

The panel itself calls check_once() periodically (each time setting the marker true and then false and reading it back to verify).

6.35 - Data plotter

A general-purpose measurement data plotting component: data source/multiple y axes, filtering, binning and fitting.

Overview

quatm/GUI/alignment/plotter.py provides the data plotter (DataPlotterWidget): it subscribes to and plots data acquired during a measurement. It supports switching the data source, an x-axis selector and multiple y-axis selectors; each y axis is processed through a configurable data chain (scaling factor → filter → binning/fitting) before being drawn in the plot area, and the result can be exported to PDF.

Components

ComponentDescription
DataPlotterWidgetMain plot: data source drop-down, x-axis selector and several y-axis selectors (right-click toggles the configuration panel)
YAxisWidgetHelpery-axis data chain: scaling factor, filter, binning and fitting

Key methods

MethodDescription
setDataSourceSets or changes the data source
checkForNewData / updateDatasourcesChecks for new data/new data sources and refreshes
plotDataAdds a set of x/y data to the plot area as a new curve
toggleConfigToggles the data source and y-axis configuration panels
exportpdfExports the current plot as a PDF suitable for archiving
clearClears the data summary and all curves in the plot area

Y-axis chain (YAxisWidgetHelper): updateYscale sets the scaling factor, the context menu offers addBinning for binning and addFitting for fitting (such as Gaussian/linear), and reevaluateData re-runs the whole data chain after a filter or similar change.

Usage

from quatm.GUI.alignment.plotter import DataPlotterWidget

plotter = DataPlotterWidget(
    axis_name="myaxis",
    prop_root="...",
    datastream_names=[".../data"],
    n_yaxis=1,
    parent=parent,
)
plotter.toggleConfig()   # expand the data-source / x, y axis selection config

7 - Communication layer

ZeroMQ Hubs, clients, properties and configuration.

Quatm’s services communicate over ZeroMQ. The design keeps the timing path independent of rendering, and makes local and remote clients interchangeable.

Hubs and streams

Each data type has a Hub that collects and redistributes messages:

  • scalar data streams;
  • image streams;
  • command channels;
  • property updates;
  • messages and logs.

Clients connect to the Hubs they need. A client process does not need to know which other clients exist, and a Hub failure is visible and restartable.

Client types

ClientPurpose
DataClientsubscribe to scalar data series
ImageClientsubscribe to camera images
CommandClientsend commands to a controller
MessageClientpublish and receive operational messages
Propertiessynchronise the distributed property tree

Properties

The property tree is the shared configuration and state model. Properties are read and written by name through a filesystem-like path; the Property Logger persists the tree and applies the persistence policy. GUI panels, web dashboards and experiment code all read the same values.

Remote property writes use a persistent publisher and explicit acknowledgement, so a remote action is not silently lost when a connection is slow.

Transport choices

  • IPC for processes on the same machine (lowest latency).
  • TCP for split control/analysis machines and remote clients.

Curve encryption is available for local and network channels. The transport layer also handles multipart framing, malformed-frame recovery and request timeouts.

Configuration

Endpoints, project root and service selection are read from the project’s configuration/ directory. A service can be launched by the Operations Console, by a project launcher, or directly as a Python module for debugging.

7.1 - Clients

Data/Image/Command clients: publish/subscribe communication (TCP/IPC), with connection targets set by the central configuration.

Overview

quatm/servers/clients.py provides the communication clients: the connection target is determined by the central configuration (Data / Image / Command Hub). GenericClient offers the standard send/receive interface and is suited to small data (<10k); use ImageClient for large payloads such as images.

Client classes

ClassConnectionDescription
GenericClientDetermined by configurationGeneral send/receive (including numpy arrays), subscribe/unsubscribe
DataClientData HubPublish and subscribe ordinary data
ImageClientImage HubSending large image-scale data (send takes a numpy array directly)
CommandClientCommand HubSends control commands to drivers (the command name is folded into the topic)
NpEncoder—JSON serialization of numpy types

Key methods (GenericClient)

MethodDescription
send(datadict, arr=None)Publish data (optionally with one numpy array)
recv()Receive one frame: (topic, dict[, array]); returns None on timeout
subscribe / unsubscribeSubscribe / unsubscribe a channel
has_new_dataNon-blocking check for new data
closeClose the PUB/SUB socket (idempotent)

Usage

from quatm.servers.clients import DataClient

dc = DataClient(name="pmt")
dc.send({"pmt": 1234}, arr=numpy_array)
topic, data = dc.recv()

7.2 - Property tree

Distributed hierarchical property tree: the local copy is synchronized bidirectionally through the Property Hub and Propertylogger.

Overview

quatm/servers/properties.py provides the property tree (Properties): it manages the configuration and properties of the control centre and maintains a synchronized copy of the property dictionary. A local set / get publishes to the Property Hub, and central changes are pushed back to the local copy. Properties are addressed like a file system (for example Drivers/xxx, or an absolute path starting with /).

Key methods

MethodDescription
set(key, value)Set a property and publish the change to the Property Hub
get(key, default=...)Read a value (always returns a deep copy; creates the default if missing)
delete(key)Delete an entry (including all its children) and broadcast the change
changes()Return the keys changed since the previous call
close / close_allClose sockets and background threads

PropertyAttribute exposes a single property of the property tree as an ordinary Python attribute (the host must have _props).

Usage

from quatm.servers.properties import Properties

props = Properties()
props.set("laser/power", 50e-3)
power = props.get("laser/power")

7.3 - Data management

Multi-stream data aggregation: aligned to each experiment run, missing streams zero-filled on timeout, then written to HDF5.

Overview

quatm/servers/datamgr.py provides data management (DataSummary): it aggregates the data streams of several input channels and assembles them into a complete dictionary for each run. Incoming data is first accumulated in incompleteData, and once the data of all subscribed channels has arrived the whole record is moved to completeData; if some data is still missing when the run ends and times out, the missing parts are filled with zeros.

Key methods

MethodDescription
initDataq(Re)subscribe to the channels configured in the properties and to the experiment start/end events
recvData / processIncomingDataReceive data and merge it into the most recent run record
savetoFileWrite the collected run dictionary to a structured HDF5 dataset
loadFromFileRead a set of stored run dictionaries back from HDF5
clearClear all data collected so far
run_foreverBlocking loop that keeps receiving data

Usage

from quatm.servers.datamgr import DataSummary

summary = DataSummary(name="sum")
summary.initDataq()     # Subscribe to channels configured in properties
summary.run_forever()   # Keep receiving; savetoFile writes the run dictionary to .h5

7.4 - Messaging

Standardized message stream client: sends timestamped, source-tagged messages to the Message Hub.

Overview

quatm/servers/messageclient.py provides the messaging system: MessageClient is the standardized message stream client, sending messages with a timestamp and originating file to the Message Hub (PUB). Everyday code should generally not construct it directly but use the module-level send functions instead.

Send functions

FunctionDescription
send_info(msg)Info level: enters the message stream as an ordinary notification
send_warning(msg)Warning level: enters the message stream and raises a warning notification
send_error(msg)Error level: enters the message stream and raises an error notification
send_debug(msg)Debug level: shown only on the debug channel

Usage

from quatm.servers.messageclient import send_info, send_error

send_info("Experiment started")
send_error("Camera connection timed out")

7.5 - Configuration and property persistence

configreader + propertylogger: reading JSON configuration, persisting the property tree and monitoring active streams.

Modules

ModuleFunction
configreader.ConfigReaderRead-only wrapper: reads configfile.json (getConfiguration())
configreader.Config()Reads configfile.json and returns the configuration dictionary
configreader.Properties()Reads the properties file properties.json and returns a dictionary
propertylogger.run_propertylogger()Property tree persistence + INIT acknowledgement (Property Logger service)
datalogger.DataStreamLoggerMonitors active data streams and writes them into the property tree
imagelogger.ImageStreamLoggerMonitors active image streams and writes them into the property tree

Usage

from quatm.servers.configreader import ConfigReader

cfg = ConfigReader()
config = cfg.getConfiguration()   # Full configuration dictionary

7.6 - Peripheral service modules

Peripheral services such as InfluxDB, notification push, the image network server, the Web Hub and the XSUB/XPUB proxy.

Service list

ModuleFunction
influxdbInfluxDB client wrapper: hardware status writes (data2db) and historical queries (query_data); can run as an RPC service
notificationsNotification push: configuration service, push_message push, post_to_mattermost compatibility entry point
image_network_serverImage network service: flushes the latest frame of each image stream to a Web figure
webhubServer entry point of the Web remote control platform (quatm.web; takes host/port from the Servers section of configfile)
xsub_xpubXSUB/XPUB forwarding server (run_server)
experiment_statusExperiment status monitoring / early-warning notifications
propertylogger / datalogger / imageloggerProperty tree persistence and monitoring of active data/image streams (see Configuration system)

Usage

from quatm.servers.notifications import push_message

push_message("Experiment finished")   # Sent through enabled push services (never raises)

8 - Deployment

Single workstation, split control/analysis machines and remote access.

Single workstation

The simplest deployment runs the GUI, services and experiment runtime on one PC. Start the project launcher; it opens the Operations Console and starts configured services. Closing the last window stops all services cleanly.

Split control and analysis

For larger experiments, run data acquisition and hardware control on the experiment PC, and analysis/GUI on a second machine:

  • configure the data and property Hubs to listen on a reachable TCP endpoint;
  • connect the analysis clients to that endpoint;
  • keep the experiment network behind a firewall or VPN.

The transport layer recovers from malformed frames and slow clients; a slow analysis machine cannot block acquisition.

Remote access

The web console can be reached over SSH tunnelling or a reverse proxy with authentication. Do not expose control endpoints directly to an untrusted network. See the web console README for token and endpoint guidance.

Production checklist

  • Pin the Python environment and driver versions.
  • Back up configuration/ and the property database.
  • Monitor disk headroom for HDF5 acquisition.
  • Verify device leases and service health after a reboot.
  • Run the release precheck and a simulated experiment before a campaign.
  • Keep the desktop and web console versions matched.

Updates

Upgrade the framework in a staging environment first. Check the changelog for incompatible changes, regenerate the project if the template changed, and verify that project-local drivers still import. scripts/check.sh contains the same gates used by CI and is a useful post-upgrade smoke test.

9 - Application examples

End-to-end examples from real experiments.

The repository contains complete experiment examples. They are intended to be read together with their project configuration, not as standalone snippets.

ExampleDemonstrates
MOT loading optimisationabsorption imaging, MOT sequence, parameter scans and live evaluation
Absorption imagingcamera handling, ROI analysis and optical-density calculation
Optical dipole traptrap loading, intensity ramps, calibration and lifetime measurement
Ion loading / state detectionion-trap control, detection thresholds and event processing
Quantum-state detectionrepeated readout, statistics and state assignment
Atom–ion interactioncombining cold-atom and ion-trap subsystems in one experiment
System calibrationreference measurements, fitting and calibration persistence

Each example project includes a README.md, configuration/ and experiments/. Start with the project launcher and run it in simulation mode before connecting hardware.

Structure of an example

  1. Physical goal — what is measured and why.
  2. Device setup — the device database entries and calibration files.
  3. Experiment class — parameters, sequence and analysis hooks.
  4. Data and analysis — expected HDF5 content and fitting.
  5. Operational notes — safety checks, common failures and tuning advice.

9.1 - Atom–ion collision experiment

Study the collision dynamics between cold atoms and a single ion with Quatm’s coordinated multi-system control.

Overview

This experiment provides the class secondinteraction: it integrates the lithium-atom MOT with ion loading, state preparation and shuttling into one atom–ion collision/interaction experiment. Each stage is encapsulated in its own method, which makes it easy to reuse and extend.

Stage methods

MethodDescription
load_ionsAblation loading + laser cooling of the ions
count_ionsFluorescence counting to determine whether an ion is in the trap
Ion_to_D_state / Ion_to_S_stateState preparation
load_MOT / MOT_offLoads / switches off the lithium MOT
ion_state_preparationIon state preparation before the interaction
runOrchestrates the loading–state preparation–collision–detection sequence

Parameters (build excerpt)

Loading_Time, Nr_Ablation_Pulses, Det_Time, Nr_Pictures, loadingtime_MOT, detection_time, RF_Voltage_Final and others.

Key code (excerpt)

from quatm.experiment import Experiment, delay, realtime

class secondinteraction(Experiment):
    def run(self):
        self.load_ions()        # ablation loading + laser cooling
        self.Ion_to_S_state()   # prepare in S1/2
        self.load_MOT()         # load the lithium MOT
        # interaction … then imaging/counting

9.2 - Dipole-trap optimisation helper

ODT alignment with the experiment helper tool: xODT alignment + stray-field compensation.

Overview

Before an optical dipole trap (xODT/cODT) experiment, the beam has to be aligned onto the atoms/ions with the dipole-trap optimisation helper. The experiment-helper main window odt_utils on the GUI side (quatm/GUI/entries/odt_utils.py) aggregates two kinds of alignment tool as tabs:

TabReal componentPurpose
xODT alignmentalignment/odt_utils.odtpiezo-mirror axis scan → Gaussian fit → publish, aligning the xODT/cODT onto the atom cloud
Stray-field compensationalignment/odt_utils.compensationfind the compensation voltage along each trap axis to minimise the stray field

Usage

from quatm.GUI.entries.odt_utils import odt_utils

helper = odt_utils()   # started as a separate process, with xODT alignment and stray-field compensation tabs

For the complete description see Experiment helper in the GUI components section; for the ODT experiment after alignment see Optical dipole trap.

9.3 - Electric-field offset compensation helper

Compensating the stray electric field of an ion trap: a GUI tool + a trap-axis voltage-scan script.

Overview

Ion-trap experiments are often affected by stray electric fields (ion-position offsets/micromotion). The electric-field offset compensation helper brings together voltage scanning, data plotting and the determination of the compensation voltage: on the GUI side it is provided by CompensationHelper in quatm/GUI/alignment/comp_helper.py; on the experiment side Usage Example/experiments/compensation.py (class compensation) applies shim voltages to the ion and triggers multi-frame images, scanning axes such as trap_x / trap_y.

GUI components (comp_helper)

ComponentDescription
CompensationCompensation main panel (one plot per trap axis + controls)
PlotWidget / TabWidgetPlot with one tab per trap axis
ControlWidget / SingleAxisWidgetScan controls per axis and “find the compensation voltage” (intersection of two fitted lines)
CompVoltageScanExecutes a voltage scan on the rf confinement

Usage

from quatm.GUI.alignment.comp_helper import CompensationHelper

comp = CompensationHelper(parent=parent)

For a fuller description see Experiment helper.

9.4 - Ion loading and cooling

A complete ion-loading routine from laser ablation to fluorescence detection, using Quatm’s multi-stage timing control.

Overview

This script provides the class loading_ion: it orchestrates the complete ion-loading routine from switching on the RF trap, producing ions by ablation and laser cooling through to fluorescence detection. The number of ablation pulses, the pulse width and the cooling/detection durations are all GUI parameters.

Parameters (build excerpt)

Nr_Ablation_Pulses, tune_pulse_number, pulse_duration, det_time, shutter_time, V_rf (trap voltage), ion_cool_power.

Timing

Switch on the RF trap (V_rf) → ablation pulses (several) → laser cooling
  → fluorescence detection (count to decide whether the ion loaded successfully)

Key code (excerpt)

from quatm.experiment import Experiment

class loading_ion(Experiment):
    def build(self):
        self.setattr_argument("Nr_Ablation_Pulses", NumberValue(...))
        self.setattr_argument("V_rf", NumberValue(...))
        # ... remainder omitted

ablation_pulse.py (single ablation), deterministic_loading.py and loading_deterministic.py (deterministic loading: retry on failure).

Companion

The cooling and fluorescence counting after ion loading can be combined with quantum-state detection for state-dependent measurements; the default parameters of the RF trap are in rf_trap.py.

9.5 - MOT loading optimisation

Automatically optimise the magneto-optical trap loading efficiency with Quatm’s parameter scans and multi-dimensional timing control.

Overview

This experiment script provides the class mot_settings: build() declares the full set of lithium MOT/CMOT devices and exposes the axial / radial magnetic-field currents and the MOT/CMOT detunings as GUI parameters, so that the magneto-optical trap loading can be optimised manually or by scanning. run() contains an empty timing sequence, on top of which imaging/counting can be added to assess the loading.

Devices (build excerpt)

DevicePurpose
li_cooling_aom / li_repump_aom / li_zeeman_aomcooling / repump / Zeeman light switches
li_cooling_power / li_repump_power / li_zeeman_powerpower of the individual beams
li_lock_mot / li_lock_cmotfrequency-lock references

Parameters (build excerpt)

MOT_Axial / _Radial, CMOT_Axial / _Radial (gradient/homogeneous-field currents), MOT_Current / CMOT_Current (trap currents), MOT_detuning / CMOT_detuning (detunings).

Key code (excerpt)

from quatm.experiment import Experiment, NumberValue

class mot_settings(Experiment):
    def build(self):
        self.setattr_device("li_cooling_power")
        self.setattr_argument("MOT_detuning", NumberValue(...))
        # ... remaining devices/parameters omitted

Loading optimisation

After changing the magnetic-field currents and the detuning, the loading efficiency can be judged from the atom number measured by fluorescence counting or absorption imaging (see Absorption imaging in the same directory).

9.6 - System calibration

Automated applications of Quatm in routine system calibration and maintenance.

Overview

Besides physics experiments, Quatm also codes routine calibration tasks as standard experiment scripts, giving one-click execution and automatic recording. This covers common calibration needs such as camera dark noise, shutter response, frequency synthesiser verification and trigger-delay calibration.

Code structure

All calibration experiments follow the same structural pattern:

  • subclass the Experiment class
  • declare the device under test and the calibration parameters in build()
  • execute the calibration procedure in run() and record the results automatically

Typical calibration tasks

Camera dark-noise characterisation

Function: acquire image frames continuously under dark conditions and characterise the dark-current noise distribution.

Main steps:

  1. Switch off all light sources and set the camera exposure time
  2. Acquire N dark frames in a loop
  3. Compute the mean and the standard deviation of each pixel
  4. Save the noise map to HDF5

Devices involved: camera

Shutter response time measurement

Function: measure the delay of a mechanical shutter from the TTL trigger to fully open/closed.

Main steps:

  1. Align a photodetector with the beam behind the shutter
  2. Trigger the shutter open with a TTL signal and start high-speed acquisition synchronously
  3. Record the rise/fall edge times of the detector signal
  4. Repeat the measurement and average

Devices involved: shutter (TTL), photodetector

Frequency synthesiser verification

Function: verify the actual output frequency and power of a Valon 5009 or DDS frequency synthesiser.

Main steps:

  1. Set the frequency synthesiser to the target frequency
  2. Measure the actual output with a spectrum analyser
  3. Scan the frequency range and record the deviation
  4. Generate a calibration curve

Devices involved: Valon 5009/DDS (serial port), spectrum analyser

Camera trigger delay calibration

Function: measure the delay between the TTL trigger and the actual exposure window of the camera.

Main steps:

  1. Share the same TTL signal between the pulsed light source and the camera trigger
  2. Scan the trigger-delay parameter
  3. Detect the position at which the light pulse appears in each frame
  4. Calibrate the optimal delay value

Devices involved: camera, pulsed light source (TTL)

9.7 - Absorption imaging

Automated acquisition and analysis of atom-cloud absorption images with Quatm’s camera control and image analysis.

Overview

This experiment script provides the class absorption_imaging: build() declares the cooling/imaging light and the camera trigger and exposes the MOT loading, the probe flash and the free-flight expansion time as parameters; run() orchestrates the imaging sequence and provides two detection modes, fluorescence_imaging and absorption_imaging.

Parameters (build excerpt)

Mot_loading, Flash_us (duration of the probe flash), expansiontime_ms (free-flight expansion), image_detuning (imaging detuning), MOT_detuning.

Devices (build excerpt)

li_cooling_aom / li_repump_aom / li_zeeman_aom (cooling/repump/Zeeman), li_absorption_h (horizontal absorption probe beam), li_imaging_valon + dds_0 (imaging frequency source), trigger_radba (trigger).

Key code (excerpt)

from quatm.experiment import Experiment, NumberValue, delay

class absorption_imaging(Experiment):
    def build(self):
        self.setattr_device("li_absorption_h")
        self.setattr_device("li_imaging_valon")
        self.setattr_argument("expansiontime_ms", NumberValue(...))
        # ... remainder omitted

    def absorption_imaging(self):
        # three frames with atoms / without atoms / background, computing the optical depth
        ...

Data analysis

The optical depth (column density) is computed and published by the analysis process ImageDivider, see Data and analysis/Absorption imaging.

9.8 - Atom–ion interaction in an optical dipole trap

Example experiment on the interaction of a neutral atom with a single ion in an optical dipole trap.

Overview

In this example we combine a lithium MOT / optical dipole trap (ODT) with the cooling, shuttling and state detection of a barium ion into one complete atom–ion interaction experiment. The experiment class interaction_optical_trapping subclasses Experiment, and the main sequence is assembled in stages by methods such as init_CMOT, absorption_imaging, expose_camera and run.

A typical sequence is roughly: load the MOT and pre-cool the ion and shuttle it “out of the way”, then load the atom cloud into the CMOT and the optical dipole trap, and after evaporation and spin polarisation move the ion back and let it interact with the atoms (loss spectroscopy or Feshbach resonance measurements are possible); finally perform optical thermometry, absorption imaging and detection of the ionic product state.

Main devices

CategoryDevice (excerpt)Purpose
Lithium MOT/CMOTli_cooling_aom / li_repump_aom / li_zeeman_aom, dds_0/1, li_mot_det_switchcooling/repump/Zeeman light and frequency, detuning switching
Magnetic fieldb_quadrupole, b_homogen, quadrupole_fet_off, homogen_fet_off, Feshbach coil relaysMOT gradient, homogeneous field, Feshbach/evaporation field
Ba lasers and statesaom_1(_ttl), aom_2(_ttl), the shutters, trigger_camion cooling, pumping, ion state detection
Optical dipole trapli_odt_* (xODT/cODT), laser poweroptical trap for atoms/ions
Imagingli_absorption_h, li_imaging_freq, trigger_absabsorption imaging and vertical trigger

Timing stages (summarised from the comments in run)

MOT loading → ion pre-cooling and shuttling (making room for the atom loading)
  → CMOT (optional high-gradient/power/detuning stage) → transfer into the ODT / two-colour trap
  → evaporative cooling (optional rf association) → spin polarisation
  → ion moved back into the atom cloud → interaction / loss / Feshbach measurement
  → trap-frequency measurement / time of flight (TOF) → ion optical thermometry → absorption imaging + ion state detection

Key code (excerpt)

from quatm.experiment import NumberValue, BoolValue, Experiment, delay, realtime

class interaction_optical_trapping(Experiment):
    def build(self):
        # devices for the lithium MOT/CMOT
        self.setattr_device("li_cooling_power")
        self.setattr_device("li_cooling_aom")
        self.setattr_device("b_quadrupole")
        self.setattr_device("b_homogen")
        # ... remaining devices omitted

    def run(self):
        # keep the ion inside the atom cloud during the interaction
        with realtime():
            self.li_cooling_aom = False
            delay(0.5)
            # ... timing omitted

Running

After placing the script in the corresponding directory under experiments/, open it in the experiment browser (Browser) to see the parameter panel generated by build; clicking run executes the whole sequence according to the timing in run. The script can also serve as a template for writing custom atom–ion interaction experiments (note the ion-shuttling ramps, the CMOT loading and the ODT transfer stages).

9.9 - Quantum-state manipulation and detection

Optical pumping and state-dependent fluorescence detection of ionic quantum states, using Quatm’s precise timing and statistical analysis.

Overview

This experiment script provides the class state_prep: it optically pumps the ion into the S1/2 or D3/2 state and can optionally apply one more cooling cycle at the end. State-dependent detection then distinguishes the bright/dark states by fluorescence counting.

Parameters

on_s_off_d (pump to S1/2 (650 off) or to D3/2 (493 off)), cooling_end (whether to add one cooling step before the end).

State-preparation logic

  • S1/2: switch off the 650 light first → the ion falls into S1/2;
  • D3/2: switch off the 493 light first → the ion is pumped into D3/2.

The same logic also appears in Ion_to_S_state / Ion_to_D_state of secondinteraction, where count_ions fluorescence counting performs the state-dependent detection (bright = S1/2, dark = D3/2, etc.).

Key code (excerpt)

from quatm.experiment import Experiment, BoolValue

class state_prep(Experiment):
    def build(self):
        self.setattr_argument("on_s_off_d", BoolValue(value=False))
        self.setattr_argument("cooling_end", BoolValue(value=True))
        # ... remainder omitted

optical_dark_pumping.py (dark pumping, pumping_time_us), d_shelving.py (D-state related measurements).

9.10 - Optical dipole trap experiment

Study the trapping and loss characteristics of cold atoms in an ODT with Quatm’s two-parameter scans and lifetime measurements.

Overview

This experiment script provides the class optical_trapping: build() declares the lithium MOT/CMOT and the imaging devices and exposes the key timing parameters for loading / trapping / imaging as GUI parameters; run() orchestrates the complete optical dipole trap (ODT) experiment sequence through methods such as init_CMOT, fluorescence_imaging and absorption_imaging.

Timing stages

MOT loading (Mot_loading) → CMOT (init_CMOT: gradient/detuning/power switching)
  → ODT trapping (trapping_time_ms) → release → time of flight (time_of_flight)
  → absorption imaging / fluorescence imaging

Parameters (build excerpt)

Mot_loading, Flash_us, trapping_time_ms, resonance_shift, image_detuning, detuning_ramp_slope_MHz_ms, time_of_flight, MOT_detuning / CMOT_detuning, MOT_Current_Loading / CMOT_Current and the Homogenous_Current_* series, etc.

Key code (excerpt)

from quatm.experiment import Experiment, NumberValue, delay

class optical_trapping(Experiment):
    def build(self):
        self.setattr_device("li_cooling_aom")
        self.setattr_device("li_absorption_h")
        self.setattr_argument("trapping_time_ms", NumberValue(...))
        # ... remainder omitted

    def init_CMOT(self):
        # CMOT stage: gradient, detuning and power switching
        ...

Companion

The xODT/cODT beam path can be aligned with the dipole-trap optimisation helper; for imaging data analysis see Absorption imaging and Absorption imaging in the data and analysis section.

9.11 - Optically trapped ion

Purely optical trapping of an ion in an optical dipole trap, with state detection.

Overview

This example uses the experiment class OpticalTrappingIon to demonstrate purely optical trapping of a single ion in an optical dipole trap. The radio-frequency trap (rf) can optionally be switched off in order to measure the performance of the pure optical trap, and optical trapping can also be performed in an adjustable magnetic field; together with ion laser cooling, spin preparation and product-state detection, the ion can also be moved into and out of the trap via the axial electrodes.

Main devices

CategoryDevice (excerpt)Purpose
Ba lasersba_493_aom(_ttl), ba_493_vert_aom, ba_650_aom(_ttl), ba_493_res_shut, ba_493_det_shut, ba_615_shutcooling, pumping (S1/2↔P1/2, D3/2, etc.), shutters
Optical dipole trapodt_power / odt_aom / odt_shutpower and on/off of the optical trapping beam
RF traprf, rf_offradio-frequency Paul trap confinement (can be switched off for a pure optical trap)
Axial movementtrap_ax, trap_ax_pc, trap_ax_wall, trap_ax_offsaxial shuttling of the ion
Magnetic fieldb_homogen, shimquantisation field for pre-cooling/trapping
Detectiontrigger_camtrigger for radial ion fluorescence/state detection

Parameters (excerpt of setattr_argument in build)

trapping_time, odtpower, rf_init and rf_ramp_duration, opt_ramp_duration, repumpers_on, pump_d32, ba_spin_prep, b_field_precooling(_applied) / b_field_opt_trap(_applied), shuttle_ion / shift_voltage, shutter_time, detect_ba_product_state and others.

Timing stages

Initialisation
  → ion spin preparation
  → magnetic-field switching (optional high-field probing)
  → (optional) shuttle the ion out and back
  → linear ramp-up of the optical trapping beam power → switch off the RF confinement (pure optical trapping)
  → switch off the optical trap (release) and the magnetic field
  → product-state/fluorescence detection

Key code (excerpt)

from quatm.experiment import *   # Experiment, NumberValue, BoolValue, delay, realtime ...

class OpticalTrappingIon(Experiment):
    def build(self):
        self.setattr_device("ba_493_aom")
        self.setattr_device("odt_power")
        self.setattr_device("rf")
        self.setattr_argument("trapping_time", NumberValue(ndecimals=0, step=10,
                             value=100, unit="µs"))
        # ... remaining parameters omitted

    def run(self):
        with realtime():
            # ramp up the optical trapping beam and switch off the RF confinement for pure optical trapping
            self.odt_power = self.odtpower
            self.rf_trap_off = True
            # ... timing omitted

Running

Once the script is placed in experiments, opening it in the experiment browser (Browser) generates the parameter panel; adjusting odtpower and trapping_time scans the power and duration of the optical trapping, while detect_ba_product_state reads out the ionic product state.

10 - Experiment framework

The experiment runtime system: task queue/execution engine, loops, sequence editing, timing inspection and run monitoring.

Overview

The experiment framework covers the complete chain from writing an experiment through scheduling to running it:

  • Experiment scripts: subclass Experiment and implement build / run (for an introduction see Writing your first experiment; for device mapping see Configuring instruments);
  • Scheduling and execution: the experiment queue (ExperimentQueue) → the execution engine (ExperimentManager) fetches tasks by priority/reservation time; the preparation station and the looper participate in enqueueing;
  • Editing and monitoring: sequence editing/simulation, FPGA timing inspection, run monitoring, the multithreaded task framework and the base window widgets.

Scheduling and execution

PageModuleDescription
Experiment Queue Managerexperiment/experiment_queueTabular task queue
Experiment Run Managerexperiment/experiment_managerExecution engine (measurement/sequence/run)
Experiment Run Schedulerexperiment/Overview of the scheduling mechanism
Experiment Preparationexperiment/prepstationPreparation station (stage/push)
Loop Managerlooper/Conditional loop execution
Process Managerentries/processmanagerProcess start/stop panel

Editing, timing and monitoring

PageModuleDescription
Sequence Editoreditors/editor_sequencerCode editing + sequence simulation
Timeline Viewerentries/experiment_timelineFPGA timing visualisation
Run Monitorstreammonitor/Data/message monitoring
Experiment Browserbrowser/Main application browser

Widgets and utilities

PageModuleDescription
Data Widgetswidgets/arg_boxesParameter input widgets
Experiment Parameter Managerentries/dashboardDefault parameter system
Collapsible Sectionswidgets/theexpandablesCollapsible sections
Preparation Queue Managerwidgets/modelsQueue/preparation table models
Worker Task Managerwidgets/browser_workersWorker threads
Multithreaded Worker Frameworkwidgets/helper_workersWorker threads
Auto-save Editoreditors/auto_save_editorPeriodic save times
Base Window Widgetswidgets/quatmQtGUI base widgets

10.1 - Data Widgets

Experiment parameter editing widgets: FloatBox/BoolBox/ComboBox, writing changes straight back to the parameter attribute.

Overview

quatm/GUI/widgets/arg_boxes.py provides data widgets: a family of input boxes that present experiment parameters as editable controls. FloatBox / BoolBox / ComboBox and the other widgets subclass the corresponding manager (FloatManager / BoolManager / ComboManager), carry a unit, range and step, and write a changed value straight back to the attribute (including last_set); they are used by the parameter grid of the experiment window and by the parameter system.

Components

ComponentDescription
FloatBoxFloating-point parameter input box
BoolBoxBoolean parameter checkbox
ComboBoxDrop-down list parameter
IntManager / FloatManager / BoolManager / ComboManager / FrequencyManagerWidget managers for the individual parameter types

Usage

from quatm.GUI.widgets.arg_boxes import FloatBox, BoolBox, ComboBox

# usually the parameter system generates the matching widget from the parameter declaration

For a fuller description see Parameter boxes and Parameter system.

10.2 - Base Window Widgets

GUI base widgets: BWidget/BFrame/BMainWindow and searchable combo boxes, etc.

Overview

quatm/GUI/widgets/quatmQt.py provides the GUI base widget classes: BWidget / BFrame / BMainWindow are the base classes of the various windows/panels/main windows (with a property namespace _props, saving the geometry to QSettings on close and restoring it on start-up); SearchComboBox and others provide searchable combo boxes with autocompletion and filtering.

Components

ComponentDescription
BWidgetBase QWidget (properties + geometry persistence)
BFrameBase QFrame
BMainWindowBase main window (QMainWindow + title/geometry persistence)
CustomCompleterCompleter supporting contains / begins matching
SearchComboBoxSearchable combo box (with a built-in SearchLineEdit)
SearchLineEditCustom line edit of SearchComboBox (select all on focus, Esc restores)

Usage

from quatm.GUI.widgets.quatmQt import BMainWindow, BWidget, BFrame

win = BMainWindow(name="My window")   # geometry persisted automatically

For a fuller description see GUI base classes.

10.3 - Sequence Editor

Experiment code editing and sequence simulation: syntax highlighting, parsing and output-channel timing visualisation.

Overview

quatm/GUI/editors/editor_sequencer.py provides the sequence editor (edit and simulate): the CodeEditorParser composite window combines the CodeEditor code editor on the left with the sequence visualisation on the right; after editing an experiment script you can simulate a run with one click and the timing of the individual output channels is drawn as step plots.

Components

ComponentDescription
CodeEditorCode editor: line numbers, Python highlighting, automatic indentation and completion
PythonHighlighter / LineNumberAreaSyntax highlighting / line-number margin
ExperimentParserParses an experiment: buildExperiment / simulateExperiment return the command stack
ExperimentSequencerDraws the sequence over time as a step plot for each output channel
CodeEditorParserComposite window: Save / Simulate / Clear toolbar + dock layout

Usage

from quatm.GUI.editors.editor_sequencer import CodeEditorParser

win = CodeEditorParser(parent=parent, filename="experiments/my_experiment.py")

For a fuller description see Sequence editor.

10.4 - Experiment Run Manager

The core experiment execution engine: takes tasks from the queue and schedules them by measurement/sequence/run level.

Overview

quatm/GUI/experiment/experiment_manager.py provides the experiment run manager (ExperimentManager): the engine that takes tasks out of the experiment queue and executes them. The queue polling runs in a worker thread, and tasks are scheduled in the hierarchy measurement (measurement) → repetition (repetition) → sequence/scan (sequence) → single run (run).

Key methods

MethodDescription
queue_fnContinuously polls the task dictionary: picks tasks by priority, task number and reservation time
runTask-run wrapper: guarantees teardown however the task ends
pausePauses / resumes the current experiment
terminate_experimentGraceful termination: lets the current run finish naturally before stopping
start_measurement / start_sequence / end_sequence / end_measurementHooks for the individual experiment stages
set_dictWrites a single parameter to an attribute of the experiment object
set_run_nr / set_rep_nrSynchronises the run / repetition number
start_queue / shutdownStarts / stops the resident queue worker

Usage

from quatm.GUI.experiment.experiment_manager import ExperimentManager

manager = ExperimentManager(browser)   # browser supplies the queue and the interface
manager.start_queue()

For a fuller description see Experiment manager.

10.5 - Experiment Browser

The main application browser: file tree, queue/preparation station/looper and MDI experiment windows.

Overview

quatm/GUI/browser/ provides the experiment browser (Browser): the main application window of Quatm, built on QMainWindow + QMdiArea. The FileSelector on the left browses the experiment scripts, the experiment queue, preparation station and looper are docked at the top, and each experiment has its own ExperimentWindow sub-window in the central MDI area.

Core components

ComponentModuleDescription
Browserbrowser/main_window.pyMain window: MDI + dock widgets + lifecycle
FileSelectorbrowser/file_selector.pyExperiment file tree (double-click to open/simulate)
ExperimentWindowbrowser/experiment_window.pySingle-experiment control panel (parameters/submit/scan)
ExperimentQueue / PrepStationexperiment/Queue and preparation station (docked at the top)
Looperlooper/Conditional looper (docked at the top)

Lifecycle

  • open_experiment: opens an experiment window; if it is already open, it is restored and brought to the front.
  • closeEvent: saves the main-window geometry and the sub-window state, then stops the worker threads.
  • moveEvent / resizeEvent: debounced saving of the geometry for restoration on the next start.

Usage

from quatm.GUI.browser.main_window import Browser

browser = Browser()
browser.show()

For a fuller description see Experiment browser.

10.6 - Experiment Queue Manager

Tabular experiment task queue: task dictionary, priority sorting, pause/termination and reserved execution.

Overview

quatm/GUI/experiment/experiment_queue.py provides the experiment queue manager (ExperimentQueue ): it shows the task queue as a table (the task dictionary expDict, keyed by task number) and supports priority sorting, state tracking and controls such as pause/terminate/delete. submit() is the only enqueueing entry point on the GUI thread (task numbers are allocated centrally, and the Looper also goes through it via the submitItem signal).

Key methods

MethodDescription
submitThe single enqueueing entry point: allocates task numbers centrally
pausePauses/resumes the currently running experiment
set_sleepingSets a task to Sleeping / Queued (reserved execution)
terminate_clicked / terminate_allGracefully terminates the selected / all tasks (Termination Pending)
delete_item / delete_clickedDeletes a task (graceful termination if it is running)
update_itemRefreshes the row of a task
set_modelReplaces the table model (ScheduleModel)

Task-state enumeration: Queued / Waiting / Sleeping / Running / Paused / Scanning / Terminating / Termination Pending / Done / Failed.

Usage

from quatm.GUI.experiment.experiment_queue import ExperimentQueue

q = ExperimentQueue(browser=parent)   # embedded in the experiment browser
q.submit(task_dict)   # enqueue (called on the GUI thread)

For a fuller description see Experiment queue.

10.7 - Worker Task Manager

QRunnable multithreaded tasks: callback execution in Worker + WorkerSignals signalling back to the main thread.

Overview

quatm/GUI/widgets/browser_workers.py provides the worker task manager: Worker subclasses QRunnable and executes a callback in the QThreadPool thread pool, then sends finished / error / result / progress signals back to the main thread via WorkerSignals. It is used by the experiment browser (experiment queue, looper, etc.); the isomorphic helper_workers is used by alignment/auxiliary tools.

Components

ComponentDescription
WorkerQRunnable worker thread: setup, execution and teardown (including exception handling)
WorkerSignalsWorker-thread signals: finished / error / result / progress, etc.

Usage

from quatm.GUI.widgets.browser_workers import Worker

worker = Worker(fn=long_running, args=(...), kwargs={...})
worker.signals.result.connect(on_result)
QThreadPool.globalInstance().start(worker)

For a fuller description see Background worker threads.

10.8 - Auto-save Editor

Editor for the list of periodic HDF5 auto-save points in time: checkbox + date/time save instants.

Overview

quatm/GUI/editors/auto_save_editor.py provides the auto-save editor (AutoSaveEditor): a popup dialog for editing the “list of points in time” of the periodic HDF5 auto-save, where every row is one (tick mark + date/time) pair and a ticked row triggers one auto-save at the corresponding instant. The configuration is stored in a property (such as regular_auto_save_times).

Components

ComponentDescription
AutoSaveEditorAuto-save point-in-time editor (BWidget popup)
DateTimeWidgetSingle-row date/time + tick widget

Key methods

MethodDescription
load_AutoSaveEditor_propsReads the date/times and tick states from the properties (static)
update_propsWrites the date/times and tick states back to the properties (static)
update_datetime / update_tickedUpdates when the date/time / tick state of a row changes
acceptSaves the changes and closes the window

Usage

from quatm.GUI.editors.auto_save_editor import AutoSaveEditor

editor = AutoSaveEditor(props=props, max_items=20, parent=parent)
editor.show()

For a fuller description see Auto-save.

10.9 - Timeline Viewer

FPGA experiment timing visualisation: a step-plot viewer of the DAC/TTL output channels over time.

Overview

quatm/GUI/entries/experiment_timeline.py provides the timeline viewer (Timeline): it loads an experiment-timing JSON file (experiment.json), plots the variation over time of each DAC/TTL output channel as a step subplot, and the output-channel check panel determines which curves are visible; it is used to debug and verify the experiment timing.

Components

ComponentDescription
TimelineTiming-viewer main window: file loading, plotting and channel control
TimelinePlotterTiming-visualisation widget: draws the state of every output in use over time
SelectorWidgetOutput-channel check panel: select all / select none

Key methods (Timeline)

MethodDescription
load_fileLoads and parses the timing JSON, builds the time series and refreshes the channel selector
plot_experimental_sequenceCreates an individual subplot per output according to the channel selection and draws a step plot
decode_ttl_bitmaskSplits a 32-bit TTL bitmask into set/clear commands for the individual channels
construct_time_sequencesConverts the step-wise timing into times/vals sequences organised per channel
reverse_device_db_transformationReverse device-database transformation: maps internal names back to readable names

Usage

from quatm.GUI.entries.experiment_timeline import Timeline

tl = Timeline()
tl.load_file("experiment.json")   # load the timing file and plot it
tl.show()

For a fuller description see Timing browser.

10.10 - Multithreaded Worker Framework

QRunnable multithreaded worker framework: callback execution in Worker + WorkerSignals signalling back to the main thread.

Overview

quatm/GUI/widgets/helper_workers.py (and the isomorphic browser_workers.py) provides the multithreaded worker framework: Worker subclasses QRunnable and puts an arbitrary callback function into the QThreadPool thread pool for execution, and returns the result to the GUI main thread through WorkerSignals using finished / error / result / progress signals.

  • browser_workers: used by the experiment browser (experiment queue, looper, etc.);
  • helper_workers: used by alignment/auxiliary and similar tools.

Components

ComponentDescription
WorkerQRunnable worker thread: setup, execution and teardown (including exception handling)
WorkerSignalsSignals a worker thread can emit: finished / error / result / progress, etc.

Usage

from quatm.GUI.widgets.helper_workers import Worker

worker = Worker(fn=long_running, args=(...), kwargs={...})
worker.signals.result.connect(on_result)   # main-thread slot
worker.signals.error.connect(on_error)
QThreadPool.globalInstance().start(worker)

For a fuller description see Background worker threads.

10.11 - Loop Manager

Conditional loop executor: automatically repeat/jump/terminate a group of tasks based on measurement results.

Overview

quatm/GUI/looper/ provides the loop manager (Looper): task entries are organised as “loop groups (tabs)”, the entries of a group are executed in order, and the jumps or termination are decided from measurement results. LoopManager runs the loop in a background thread; the loop definition is persisted in loopfile.json and shared with the web remote-control platform.

Core logic

LoopManager.run_loop(): start from the first group, loop indefinitely until terminate
  └─→ execute the entries of the group in order
       ├─ Task: submit to the experiment queue, advance once complete (maximum count configurable)
       └─ Conditional: read data-stream fields and compare → jump if satisfied / else branch otherwise

Components

ComponentDescription
LoopGroupLoop group (one tab)
TaskItem / ListItemTask entry / task-list entry
ConditionalItem etc.Condition entries such as data conditions, arrival index and time
BaustellePanel for adding/removing/editing the entries of a group
DataManagerSubscribes to data streams and accumulates the latest measurement values
LoopManagerLoop executor (run_loop() / terminate(), background thread)

Usage

from quatm.GUI.looper import Looper

looper = Looper(parent=browser)   # looper tab of the dock at the top of the browser

For a fuller description see Looper.

10.12 - Preparation Queue Manager

Table models for prepared tasks (preparation station): the list model PrepModel and the queue ScheduleModel.

Overview

quatm/GUI/widgets/models.py provides base classes that map Python dictionaries / lists onto Qt table models (derived from the ARTIQ models). The preparation queue (preparation station) uses the list-type PrepModel: prepared tasks need no unique ID and their order can be changed freely; the experiment queue uses the dictionary-type ScheduleModel (sorted by priority, then by task number).

Core classes

ClassDescription
_SyncSubstructHelper wrapping the dictionary operations of the underlying container (append/insert/pop callbacks refresh)
DictSyncModelDictionary → table model base class
ListSyncModelList → table model base class
ScheduleModelExperiment queue (ExperimentQueue) model: tasks sorted by priority/task number
PrepModelPreparation queue (PrepStation) model: list type, order adjustable

Concrete business models implement three hooks in a subclass: convert (key/value/column to cell content), sort_key (sorting key) and flags (edit flags).

Usage

from quatm.GUI.widgets.models import PrepModel

model = PrepModel(init=prep_list)   # list of prepared tasks

The models are described in Data models; preparation-queue operations are described in Preparation station.

10.13 - Experiment Run Scheduler

The run scheduling mechanism: central enqueueing in the queue, workers fetching tasks by priority/reservation time.

Overview

Experiment run scheduling is accomplished by the experiment queue and the execution engine working together: submit() of the ExperimentQueue is the only enqueueing entry point on the GUI thread (task numbers are allocated centrally, and the preparation station and looper also enqueue through it); the resident worker polls the task dictionary in ExperimentManager.queue_fn and selects and executes tasks by priority → task number → reservation time (due_check) (measurement → sequence → run).

ComponentModuleDescription
ExperimentQueueexperiment/experiment_queue.pyTask queue: enqueueing, state tracking, pause/termination
ExperimentManagerexperiment/experiment_manager.pyExecution engine: picks tasks, runs measurement/sequence/run
PrepStationexperiment/prepstation.pyPrepared tasks, pushed into the queue
Looperlooper/Conditional loop, enqueued via submit

Scheduling flow

PrepStation / Looper / experiment window --submit()--> queue task dictionary
        ↓
ExperimentManager.queue_fn (worker thread, by priority/task number/reservation time)
        ↓
start_measurement → start_sequence → several runs → end_sequence → end_measurement

At run time pause / resume is supported (pause) as well as graceful termination (terminate_experiment, Termination Pending).

10.14 - Experiment Preparation

The preparation station: staging, editing, ordering and pushing tasks before they are sent to the queue (prepfile persistence).

Overview

quatm/GUI/experiment/prepstation.py provides experiment preparation (PrepStation): the preparation station in the dock at the top of the experiment browser, used for staging and editing tasks before they are sent to the experiment queue. The task list is displayed with a PrepModel table (its order can be changed freely) and persisted to prepfile.json.

Key methods

MethodDescription
push / push_row / push_firstPushes the selected/designated/first task to the experiment queue
push_to_looperPushes the selected task to the looper
move_up / move_downChanges the order of tasks
delete_clicked / set_sleepingDeletes / sleeps a task
open_editor / open_viewer / save_paramsEdits parameters, views parameters, saves them as JSON
update_prep_fileSaves the task list to prepfile.json
load_previousRestores the tasks of the previous session from prepfile.json

Usage

from quatm.GUI.experiment.prepstation import PrepStation

ps = PrepStation(browser=parent)
ps.push_first()   # push the first prepared task into the queue

For a fuller description see Preparation station.

10.15 - Process Manager

Distributed process panel: start/stop background processes grouped as Servers/Driver/GUI/Viewer/Monitor.

Overview

quatm/GUI/entries/processmanager.py provides the process manager: it reads the configuration grouped as Servers / Driver / GUI / Viewer / Monitor and generates a SingleProcess start/stop button panel for every script entry, managing the whole set of experiment-software processes in one place. A process is started with the current interpreter, either as a script or as a module (python -m), and carries the project root and the parent-process watchdog environment.

Components

ComponentDescription
ProcessManagerCategory panel: reads the configuration and generates a start/stop button for every entry
SingleProcessSingle-entry panel: start / asynchronous termination / status polling colouring

Key methods (SingleProcess)

MethodDescription
startProcessStops the old instance, then starts the child process with the current interpreter
stopProcessTerminates the child process asynchronously without blocking the GUI thread
updateStatusPeriodically polls the exit status and colours it (running / exited / crashed)

Usage

from quatm.GUI.entries.processmanager import ProcessManager

pm = ProcessManager(parent=parent)
pm.show()

For a fuller description see Process manager.

10.16 - Experiment Parameter Manager

The default experiment parameter system: DefaultExp aggregates device parameters, Dashboard edits them grouped by device.

Overview

quatm/GUI/entries/dashboard.py provides the experiment parameter manager: DefaultExp is the experiment base class that aggregates the attributes of all devices (build() walks the device database and groups them by type), and the per-type parameter managers (BasicManager / IntManager / FloatManager / BoolManager / ComboManager / FrequencyManager) generate input widgets with unit/range from the parameter declarations; the Dashboard panel presents the device groups as editable default-parameter sections.

Components

ComponentDescription
DefaultExpDefault experiment: aggregates all device attributes and groups them by device
DashboardDefault-parameter panel: generates parameter widgets grouped by device
BasicManager etc.Per-type parameter-widget managers (Int/Float/Bool/Combo/Frequency)

Usage

from quatm.GUI.entries.dashboard import Dashboard

panel = Dashboard(parent=parent)   # show the default parameters grouped by device

For a fuller description see Parameter system.

10.17 - Run Monitor

Data-stream monitoring GUI: data/image/command/message multi-tab monitoring and single-stream panels.

Overview

quatm/GUI/streammonitor/ provides the run monitor: MainWindow hosts monitoring tabs for data streams (Data), image streams (Image), commands (Command), the message log (Message) and history (History); each monitoring tab consists of a StreamMonitor that subscribes to a single data stream and displays it in real time.

Components

ComponentDescription
MainWindowMonitoring main window: multi-tab container + menu
StreamMonitorSingle-stream monitoring panel: subscribes to one data stream and displays it in real time
MessageTableModelMessage-log table model (timestamp/source/level/content, coloured by level)
ImageTableModelImage-stream metadata table (fixed time/source columns + dynamic key columns)

Usage

from quatm.GUI.streammonitor import MainWindow, StreamMonitor

win = MainWindow(parent=parent)   # multi-tab monitoring main window
panel = StreamMonitor(name="PMT", stream_type="Data", parent=parent)

For a fuller description see Data stream monitor.

10.18 - Collapsible Sections

Collapsible-section dialog: SectionExpandButton toggling + CollapsibleDialog defining the sections.

Overview

quatm/GUI/widgets/theexpandables.py provides collapsible section components: CollapsibleDialog (a QDialog subclass) organises several collapsible sections with a QTreeWidget whose header is hidden; each section header is a SectionExpandButton button, and clicking it expands/collapses the corresponding content.

Components

ComponentDescription
SectionExpandButtonButton that expands/collapses a section (QPushButton)
CollapsibleDialogBase class for dialogs to which collapsible sections can be added

Key methods (CollapsibleDialog)

MethodDescription
define_sectionsOverride this method to define all sections as (title, widget) tuples
add_sectionAdd one collapsible section
add_button / add_widgetCreate a top-level entry containing a button / attach a content widget

Usage

from quatm.GUI.widgets.theexpandables import CollapsibleDialog

class MyDialog(CollapsibleDialog):
    def define_sections(self):
        self.add_section("Parameters", some_widget)
        self.add_section("Advanced", advanced_widget)

dlg = MyDialog()
dlg.define_sections()
dlg.show()

For a fuller description see Collapsible components.

11 - Best practices

Patterns for reliable experiment code and safe operation.

Separate configuration from code

Device addresses, calibration files and channel maps belong in configuration/, not in experiment source. A run should be reproducible from the project directory and its data file.

One responsibility per class

Keep experiment logic, device communication and analysis separate. A driver should not know about a specific experiment; an experiment should not open sockets directly. This makes simulation and reuse possible.

Declare parameters, do not hard-code

Use NumberValue, BoolValue and StringCombo for anything an operator may change. The same declarations drive the GUI, the web dashboard and scan validation.

Make cleanup explicit

Acquire devices through the framework, use leases, and close clients in close(). Do not rely on process exit for hardware cleanup. Long-running services should stop timers and worker threads in closeEvent.

Keep timing deterministic

Do not perform file I/O, plotting or network calls inside a timed sequence. Prepare data before the run and hand results to analysis asynchronously.

Automate checks

Run scripts/check.sh before committing. Add regression tests for experiment semantics, not only for helper functions. Use simulation for routine CI and reserve real hardware for verification runs.

Operate safely

Before an unattended campaign, verify:

  • simulated run of the exact scan;
  • disk headroom and save acknowledgement behaviour;
  • device leases and health;
  • alerting and remote access;
  • a documented stop procedure.

12 - Physics models

Atom database, optical dipole traps, Stark shifts and scattering.

quatm.physics collects reusable models shared by experiments and analysis. They are ordinary Python functions: no hardware access, no GUI dependency, and suitable for notebooks or offline design work.

Included models

  • Atom database — species data, transition wavelengths, linewidths and saturation parameters.
  • Optical dipole traps — trap depth, frequencies, waists and polarisability.
  • Light–matter interaction — scattering rate, saturation and photon recoil.
  • Stark shifts — AC and DC shifts for ground and excited states.
  • Feshbach resonances — resonance lookup and field-dependent scattering length.
  • Statistics — distributions and fitting helpers used by analysis code.

Using the models

from quatm.physics.odt import optical_trap

result = optical_trap(power=1.0, waist=30e-6, wavelength=1064e-9,
                      species='Rb87')
print(result.depth_uK)

The model layer imports no GUI and no instruments, so it can run on a laptop or inside a web service. Units are explicit in function signatures and docstrings; avoid ambiguous bare numbers in experiment code.

Relationship to analysis

Analysis modules use the same physics layer for conversion between camera counts, optical density and atom number. This keeps calibration and fitting consistent across the GUI, web console and scripts.

12.1 - Atom database

Atom properties for Ba, Rb, Li, Mg and Ca: mass, transition frequencies and Clebsch-Gordan coefficients.

Overview

atomlib provides precomputed physical data for the atomic species commonly used in cold-atom and ion experiments.

Predefined atoms

DictionarySpeciesTypical use
BariumDictBa⁺Ion-trap quantum computing
RubidiumDict⁸⁷RbBose-Einstein condensates
LithiumDict⁶LiUltracold Fermi gases
MagnesiumDictMg⁺Ion-trap precision measurement
CalciumDictCa⁺Ion-trap optical clocks

Attribute fields

AttributeDescription
massAtomic mass (kg)
ground_stateGround-state level identifier
excited_stateExcited-state level identifier
transition_frequencyTransition frequency (Hz)
decay_rateSpontaneous-emission decay rate (s⁻¹)
clebsch_gordanClebsch-Gordan coupling coefficient

12.2 - Electric-field calculations

Electric potentials in ion traps: stray fields, curvature and one-dimensional ion-position calculations.

Overview

efields computes the electric potential distribution of ions in a Paul trap. It supports stray-field and curvature parameters and solves for the one-dimensional ion equilibrium positions using the James method.

Classes

ClassDescription
efieldsElectric-potential calculation, including stray field and curvature
DCTrapManages mean/random stray-field configurations
from quatm.physics.odt.efields import efields

field = efields(stray_field=(1e-3, 0, 0), curvature=0.01)
potential = field.compute_potential(x_grid)

12.3 - Laser model

Gaussian-beam laser: beam size, intensity, wavefront curvature and scaled electric field.

Overview

The Laser class models a Gaussian-beam laser. It manages the automatic synchronization of wavelength/frequency/wavenumber conversion and computes the beam size, the radius of curvature R and the scaled electric field along the propagation direction.

from quatm.physics.odt.lasers import Laser

laser = Laser(wavelength=1064e-9, power=10.0, waist=50e-6)
intensity = laser.intensity(z=0.0)
w = laser.beam_radius(z=0.01)

12.4 - Light-matter interaction

AC Stark shifts and scattering rates: Clebsch-Gordan-weighted calculations for S→P and D→P transitions.

Overview

LMinteraction computes the interaction between a laser field and an atom: the AC Stark shifts for the ground-to-excited (S→P) and metastable-to-excited (D→P) transitions, and the off-resonant photon scattering rate.

from quatm.physics.odt.lightmatterint import LMinteraction

lm = LMinteraction(atom=Ba, laser=laser)
shift = lm.stark_shift(intensity=1e6)  # Hz
rate = lm.scattering_rate(intensity=1e6)  # s⁻¹

12.5 - Optical trap model

Composite traps: full 1D/2D/3D potential calculation for dipole beams plus electric fields.

Overview

Trap combines a Gaussian-beam optical dipole trap with electrostatic fields into a composite trap model and computes the trap depth (potential turning points) and the three-dimensional potential distribution.

from quatm.physics.odt.opticaltraputils import Trap

trap = Trap(laser=odt_laser, efield=dc_field)
depth = trap.trap_depth()  # µK
potential_2d = trap.potential_2d(x, y, z=0)

12.6 - Statistical tools

Binomial Wilson score intervals: confidence intervals for ion-state discrimination.

Overview

statistics provides binomial proportion confidence intervals based on the Wilson score interval, used for statistical error estimation in ion-state discrimination.

FunctionDescription
wilson_score(p, n)Computes the Wilson score for proportion p
wilson_err(p, n)Half-width of the Wilson confidence interval
getrandbincont(p, n, N)Samples the experiment probability N times from binomial outcomes

Wigner symbols (utils)

The utils module provides complete Racah-formula implementations of the Wigner3j and Wigner6j symbols, for angular-momentum coupling calculations.

13 - Contributing

Issues, pull requests and review expectations.

Quatm is developed with a small core team and contributions from laboratories. We welcome bug reports, documentation fixes, driver additions and well-scoped improvements.

Before opening an issue

  • Search existing issues and pull requests.
  • Include the commit or version, operating system, Python version and the affected application.
  • For hardware issues, include the driver, instrument model, connection type and relevant logs.
  • Do not paste licence keys, tokens or personal data.

Pull requests

  • Keep changes focused; do not mix unrelated refactors with a fix.
  • Follow the repository conventions in docs/CONVENTIONS.md and AGENTS.md.
  • Add or update tests for behaviour changes.
  • Run bash scripts/check.sh and report the exit code.
  • Update documentation and translations when user-facing strings change.

Review

The review checks correctness, experiment semantics, resource lifecycle, licensing boundaries and test coverage. Changes that affect timing or units require an explicit rationale. Large architectural changes should start with an ADR under docs/adr/.

Security

Do not report security vulnerabilities in public issues. Contact security@quatm.com; see the security policy.

14 - Monitoring

Environmental and instrument-health monitoring.

Long-running experiments need more than the main data stream. Quatm monitors the laboratory infrastructure and the health of its own services.

Environmental monitors

Bundled monitors cover:

  • temperature controllers and sensors;
  • magnetometers / Hall probes;
  • wavemeters and laser power meters;
  • vacuum gauges;
  • PC health (CPU, memory, disk);
  • oscilloscopes and general SCPI instruments.

Monitors are ordinary services that publish into the property and data streams. A failed monitor is reported and can be restarted without touching the experiment runtime.

Device health

The Device Manager can probe instruments, run driver self-tests and show the current lease owner. Health state and history are kept separately from experiment data, so a failing instrument does not corrupt a run’s HDF5 file.

Service health

The Operations Console and Task Monitor show:

  • service process state and uptime;
  • task queue state and heartbeats;
  • configured endpoints and reachability;
  • data-manager save errors and disk headroom;
  • resource alerts (disk, database size, threads, file descriptors).

Alerts are rate-limited and transition-based: an ongoing error is reported once, and recovery is reported separately.

14.1 - LFI3751 temperature controller

Wavelength Electronics LFI3751 temperature controller driver: PyVISA communication.

Overview

LFI3751 drives the Wavelength Electronics high-precision semiconductor laser temperature controller over PyVISA.

from quatm.monitor.LFI3751 import LFI3751

tc = LFI3751(addr="USB0::...")
temp = tc.get_temperature()
tc.set_setpoint(25.0)

14.2 - Gaussmeter

PCE-MFM 3000 gaussmeter driver: serial communication, real-time magnetic-field measurement.

Overview

PCE_MFM_3000 drives the PCE Instruments magnetic flux density meter and reads the magnetic induction in real time over a serial port.

from quatm.monitor.PCE_MFM_3000 import PCE_MFM_3000

gauss = PCE_MFM_3000(port="/dev/ttyUSB0")
field = gauss.get_field()  # Returns the value in mT

14.3 - Cavity environmental sensors

Arduino temperature, humidity and pressure sensors: real-time monitoring of the cavity environment.

Overview

The arduino driver reads temperature, humidity and pressure sensor data from inside the cavity through an Arduino Nano.

from quatm.monitor.cavity_arduino import arduino

env = arduino(port="/dev/ttyACM0")
temp, hum, press = env.read_all()

14.4 - PC health monitoring

Real-time CPU/memory/disk/network monitoring based on psutil.

Overview

PC_health uses psutil to monitor the CPU utilization, memory usage, disk space and network traffic of the experiment control computer, preventing experiment interruptions caused by resource exhaustion.

from quatm.monitor.pc_health import PC_health

health = PC_health()
cpu = health.get_cpu_percent()
mem = health.get_memory_usage()
disk = health.get_disk_free()

14.5 - Oscilloscope

Rigol DS1054Z oscilloscope driver: PyVISA waveform acquisition and voltage measurement.

Overview

The ds1054z driver controls the Rigol DS1054Z four-channel digital oscilloscope and performs waveform acquisition and automatic measurements over PyVISA.

from quatm.monitor.ds1054z import ds1054z

scope = ds1054z(addr="USB0::...")
waveform = scope.get_waveform(channel=1)
vpp = scope.measure_vpp(channel=1)

15 - Utilities

Quatm general-purpose utility functions: variable density sampling and other helpers

Overview

The quatm.utils package provides general-purpose utility functions used across the modules of the Quatm framework.


Variable density sampling — quatm.utils.vds

Variable density sampling (VDS) allows efficient exploration of a parameter space by concentrating the sampling points in the regions of interest (such as near resonances or phase transitions in quantum systems).

Core idea

In a physics experiment, some parameter regions are more meaningful than others. For example:

  • magnetic-field scans near a Feshbach resonance
  • frequency scans near the centre of a spectral line
  • parameter exploration near a phase-transition critical point

By automatically densifying the sampling points in the regions of interest and sampling sparsely in flat regions, VDS extracts the maximum information with the minimum number of data points.

gauss_prime_norm — normalised Gaussian derivative

The derivative of a Gaussian function, normalised to a maximum of 1:

$$f(x) = \sqrt{e} \cdot \frac{x - \mu}{\sigma} \exp\left(-\frac{(x-\mu)^2}{2\sigma^2}\right)$$

from quatm.utils.vds import gauss_prime_norm

# evaluate the Gaussian derivative at x=5.0
val = gauss_prime_norm(5.0, mu=5.0, sigma=1.0)
ParameterDescription
xinput value, either a scalar or a numpy.ndarray
mucentre of the Gaussian
sigmastandard deviation of the Gaussian

gauss_prime_norm_abs — absolute-value normalised Gaussian derivative

The normalised form of the absolute value of the Gaussian derivative, suitable as an interest function for symmetric peak detection.

create_support_points_stochastic — stochastic support-point generation

Starts from a fine equidistant grid and randomly removes the points in regions of low interest, keeping the points in regions of high interest.

from quatm.utils.vds import (
    create_support_points_stochastic,
    gauss_prime_norm_abs
)

# define the interest function: concentrate the sampling around x=5.0
def interest(x, center, width):
    return gauss_prime_norm_abs(x, center, width)

points = create_support_points_stochastic(
    start=0.0,      # scan start
    stop=10.0,      # scan stop
    steps=50,       # number of final support points
    interest_fn=interest,
    max_stepsize=0.5,   # maximum permitted step size
    enhancement=0.8,    # 0-1, 1 means strong enhancement
    center=5.0,         # passed to interest_fn
    width=1.0           # passed to interest_fn
)
ParameterDescription
startscan start, always included in the final support points
stopscan stop, always included in the final support points
stepsnumber of final support points
interest_fninterest function f(x, **kwargs) returning 0 (low interest) to 1 (high interest)
max_stepsizemaximum spacing between adjacent support points
enhancement0-1, 1 means strong enhancement of the regions of interest, 0 means uniform random sampling
**kwargsadditional arguments passed to interest_fn

Application scenarios

Variable density sampling is particularly suitable for:

  • Feshbach resonance spectrum scans: densify the sampling near the resonance peak
  • Spectral line scans: improve the resolution near the line centre
  • Phase-transition parameter scans: explore the critical region finely
  • Trap-frequency measurements: concentrate the sampling near the expected resonance frequency

15.1 - Variable density sampling

Variable density sampling: a support-point generator that densifies the sampling in parameter space according to an interest function.

Overview

quatm/utils/vds.py provides variable density sampling (VDS) tools: it generates support points (sample points) that are randomly distributed and densified in the regions of interest according to a given interest function, so as to optimise the limited experiment time.

Functions

FunctionDescription
create_support_points_stochastic(start, stop, steps, interest_fn, ...)Generates randomly distributed support points densified in the ROI
gauss_prime_norm(x, mu, sigma)Normalised form of the derivative of a Gaussian function (peak normalised to 1)
gauss_prime_norm_abs(x, mu, sigma)Normalised form of the absolute value of the Gaussian derivative

Usage

from quatm.utils.vds import create_support_points_stochastic

# interest_fn returns a weight (0-1) that determines the sampling density at each position
points = create_support_points_stochastic(
    start=0, stop=100, steps=200,
    interest_fn=lambda x: gauss_prime_norm_abs(x, mu=50, sigma=10),
)

16 - Web remote control

The browser console, mobile operation and synchronisation with the desktop.

The web console is installed with the core package (quatm.web) and served by the Web Hub. It is not a reduced viewer: it shares the same experiment queue, scans, preparation file, looper and property tree as the desktop GUI.

Capabilities

  • Monitoring — live curves, message log and camera images.
  • Experiments — experiment browser, experiment windows and a queue synchronised with the desktop.
  • Dashboard — instrument parameters grouped by device, with units, limits and steps.
  • Data — per-run live tracking, HDF5 archive browsing, fits and CSV export.
  • Properties — browse and edit the shared property tree.
  • Analysis — start and stop analysis jobs shared with the desktop.
  • Timeline — FPGA sequence visualisation and device mapping.
  • Notifications — push notification configuration.
  • Processes — view and control services, drivers and GUI processes on the experiment PC.

Remote access

The Web Hub binds to a configurable endpoint. For remote access use an SSH tunnel or a reverse proxy with authentication; never expose an unauthenticated control endpoint to an untrusted network. See the Web console README for endpoint, token and firewall guidance.

Mobile operation

The layout adapts to phones and tablets: horizontally scrolling navigation, single-column stacking, larger touch targets and charts sized for small screens. The same permissions and data model apply as on the desktop.

Synchronisation

Because both clients use the same Hubs and message protocol, an action on one client is immediately visible on the other. Queue entries, scan definitions, preparation state, loop logic and property values cannot diverge between the desktop and the browser.