Simons Observatory: UK (SOUK) uses Microwave Kinetic Inductance Detectors (MKIDs) to observe the cosmic microwave background across two telescopes. Each MKID is a superconducting resonator whose resonance frequency shifts in response to absorbed photons - the readout system's job is to continuously probe these resonances and record the detector responses.
The readout hardware is based on AMD/Xilinx RFSoC (Radio Frequency System-on-Chip) boards. Each board runs the souk-firmware dual-pipeline firmware providing two independent RF readout channels, each capable of driving and measuring up to 2048 tones simultaneously across a wide RF bandwidth. The full SOUK instrument uses 7 boards per telescope (28 RF networks in total), with all boards managed from a single host machine.
souk_readout_tools is the Python control and data acquisition package for this system. It uses a client-server architecture with ReadoutClient and ReadoutServer classes:
- The server runs on each RFSoC's ARM processor. It interfaces with the FPGA firmware through the souk_mkid_readout library and controls RF peripheral hardware (attenuators, amplifiers, etc.) via
souk-peripherals-control. One server instance runs per pipeline (two per board), managing hardware state, configuration, and data tranmission. - The client runs on a remote machine and connects to an individual pipeline's server over TCP. It provides the high-level API for measurements, data acquisition, and basic analysis tools for inspecting data quality.
Client Machine (OCS) RFSoC Board
┌─────────────────────┐ ┌──────────────────────────────────────────────────────┐
│ Python/IPython │ │ ARM Processing System (PS) │
│ │ │ │
│ │ │ ┌── Ubuntu 24.04 (CASPER image) ───────────────┐ │
│ │ │ │ │ │
│ ReadoutClient A ─┼─── TCP ────┼───┼── ReadoutServer 0 ────┐ │ │
│ │ │ │ ├── souk_mkid_readout │ │
│ ReadoutClient B ─┼─── TCP ────┼───┼── ReadoutServer 1 ────┘ │ │ │
│ │ │ └───────────────────────────────────┼──────────┘ │
│ ReadoutClient C ─┼─ ├───────────────────────────────────────┼──────────────┤
│ │ │ FPGA Programmable Logic (PL) │ │
│ ... │ │ │ │
│ │ │ ┌── SOUK Firmware ──────────────────┴──────────┐ │
│ ReadoutClient N ─┼─ │ │ │ │
│ │ │ │ Pipeline 0 Pipeline 1 │ │
│ │ │ └───────────────────────┼──────────────┼───────┘ │
│ │ ├───────────────────────────┼──────────────┼───────────┤
│ │ │ RF Data Converter │ │ │
│ │ │ (RFDC) ┌──┴──┐ ┌──┴──┐ │
│ │ │ DAC0 ADC0 DAC1 ADC1 │
└─────────────────────┘ └────────────────────────┼─────┼────────┼─────┼────────┘
TX0 RX0 TX1 RX1
Each ReadoutServer uses two TCP ports - a request port for JSON command/response control and a stream port for binary data transfer.
- Tone control - set frequencies, amplitudes, and phases for up to 2048 readout tones per pipeline with full TX/RX power calibration through the entire signal chain (DSP → DAC → RF frontend → cryostat → detector). Tone updates happen on millisecond timescales, enabling fast resonator tracking.
- Blind tone management - add fixed off-resonance monitor tones for gain/phase tracking, with config-based and interactive helpers, sweep inclusion, calibrated power support, and retune-safe tone metadata.
- Data acquisition - discrete samples, continuous streaming, triggered streaming, and single tone snapshots (1024 sample bursts at the pre-accumulator rate). All data includes PTP telescope timestamps from the firmware for precise time synchronisation.
- G3 stream output - record continuous streams to so3g/spt3g
.g3files (Observation, Wiring, Scan frames withG3SuperTimestreampayloads) for downstream OCS / SO data pipelines. - Mock server mode -
ReadoutClient(mock=True)emulates the readout server in-process (synthetic resonator catalogue, sweeps, snapshots, streams) for OCS / controller integration testing without RFSoC hardware. - Frequency sweeping - wideband survey sweeps across the full RF band and targeted sweeps around individual resonances
- Resonance finding - automated peak detection across multiple data formats (magnitude, phase, group delay, |dS21/df|, etc), plus an interactive PyQt5 GUI
- Resonance fitting - linear and Duffing-capable complex S21 fitting (
fit_resonance,batch_fit,fit_sweep_stack) with extracted physical parameters (fr,Qi,Qc,phi, etc.), optional uncertainty weighting, and process-parallel fitting support.batch_fitconsumes server sweep dictionaries and preserves tone metadata;fit_sweep_stackconsumes already-windowed arrays and fits rows independently. - Retuning - sweep-and-retune workflows to track drifting resonances using max-derivative or min-magnitude methods
- Fast frequency modulation - dither each tone over a few probe points (2-3) every accumulation and stream the samples tagged per point, for real-time IQ → frequency/dissipation conversion (live per-resonator
dφ/dfrather than a stale start-of-obs calibration) and inflection-point resonator tracking. Toggle on/off at any time and update centres/offsets live with no dropped frames; thesouk_readout_tools.modulationtoolkit demodulates in the de-embedded/phase-centred basis (params_from_sweep→group_cycles→demodulate). See Fast Frequency Modulation. - Power management - automatic TX/RX level optimisation with saturation detection, dynamic range management, and calibrated power control in dBm at any reference plane in the signal chain
- ADC calibration freeze - freeze the RFSoC's internal ADC calibration during observations to eliminate drift noise, with periodic defrost for recalibration
- Clock source control - select internal (12.8 MHz) or external (10 MHz) PL reference clock with PLL lock status monitoring
- PTP/NTP timing status - packaged
ptp4l, chrony, and timing-monitor service templates, plusget_info("timing")for checking GM lock, PHC/NTP source health, and firmware sync readiness - Timed sync & multi-board alignment (v7.10) - TT-targeted firmware syncs so independent boards align timestamps and accumulations to the same epoch, with on-board 1-PPS generation from the TSU strobe path. See TSU Strobe and Timed Sync.
- Dual-pipeline support - two independent pipelines per board with three-level initialisation (program FPGA → shared resources → per-pipeline resources) to prevent cross-pipeline disruption
- VACC multitone (v7.9+) - multiple tones per FFT bin with automatic LO index management and sparse tone index handling
- Parameter-series measurements - repeat any measurement across an external parameter (attenuation, temperature, bias, time, ...) with run directories, resume, retries, and live summaries; power sweeps are the worked example. See Parameter-Series Measurements.
- Configuration sync - YAML-based config with
push_config()/pull_config()for client-server synchronisation, including automatic calibration file transfer - Server infrastructure - async TCP server with systemd daemon support, multi-client streaming, and remote status monitoring
- Analysis utilities - built-in tools for parsing raw data, plotting in various formats, fitting resonance and noise models, and extracting detector parameters.
Most users only need the client. The server should already be running on the RFSoC.
Clone the repository and initialise submodules:
git clone https://github.com/sr-cdf/souk_readout_tools
cd souk_readout_tools
git submodule init && git submodule updateCreate a python virtual environment and install:
python3 -m venv client_venv
source ./client_venv/bin/activate
pip install .On Windows, use python -m venv and .\client_venv\Scripts\Activate.ps1 instead.
No RFSoC hardware? Pass mock=True to run against a built-in in-process mock
server — no config or address needed — to explore the API or develop analysis
code without a board:
from souk_readout_tools.client.readout_client import ReadoutClient
client = ReadoutClient(mock=True)
client.ensure_ready()
freqs = [0.800e9, 1.500e9]
client.set_tone_frequencies(freqs)
client.set_tone_phases(client.generate_newman_phases(freqs)) # Newman phases minimise the multi-tone crest factor
data = client.parse_samples(client.get_samples(500), num_tones=2)Otherwise, connect to a real running server and pull its config:
from souk_readout_tools.client.readout_client import ReadoutClient
client = ReadoutClient(address='10.11.11.11', request_port=10000)
client.pull_config(save_as='my_config.yaml')Or create a config from the bundled template:
from souk_readout_tools.config_utils import copy_template_config
copy_template_config('my_config.yaml', pipeline_id=0)Then connect, initialise, set tones, and acquire data:
client = ReadoutClient(config_file='my_config.yaml')
client.ensure_ready()
freqs = [0.800e9, 1.500e9]
client.set_tone_frequencies(freqs) # frequencies in Hz
client.set_tone_powers([-90, -95], reference_plane='detector') # powers in dBm
client.set_tone_phases(client.generate_newman_phases(freqs)) # Newman phases minimise the multi-tone crest factor
raw = client.get_samples(500)
data = client.parse_samples(raw, num_tones=2)
# Plot the timestream
from souk_readout_tools.plotting import plot_timestream
plot_timestream(data)For full installation details (including server setup, SD card imaging, and daemon configuration), see the Installation Guide. For RFSoC timing setup, see Timing and PTP.
Client (installed on your machine):
| Command | Description |
|---|---|
souk-connection-test |
Test connectivity to the readout server (-C config.yaml or -a address) |
souk-wideband_sweep |
Perform a wideband frequency sweep |
souk-mkid-finder-app |
Launch the interactive MKID resonance finder GUI |
souk-batch-snapshots |
Acquire pre-accumulator snapshots across tones |
souk-find-resonances |
Find resonances from sweep data |
Server (installed on the RFSoC):
| Command | Description |
|---|---|
souk-readout-server |
Start the readout server (optional config path, -p for pipeline) |
souk-enable-daemon |
Enable the server as a systemd service (-p 0 1 for both pipelines) |
souk-enable-daemons |
Enable both pipeline systemd services |
souk-disable-daemon |
Disable the server systemd service(s) |
souk-disable-daemons |
Disable both pipeline systemd services |
souk-restart-daemon |
Restart the server systemd service(s) without reinstalling them |
souk-restart-daemons |
Restart both pipeline systemd services without reinstalling them |
souk-enable-timing |
Install and restart the packaged ptp4l, chrony PHC, and timing-monitor setup |
souk-timing-monitor |
Run the PTP/NTP timing monitor daemon |
souk-test-timing-monitor |
Query or stream status from /run/timing-monitor.sock |
souk-find-attenuators |
Discover RUDAT and SOUK mixerless-module attenuators (--status for live values) |
souk-find-bypass-amps |
Discover SOUK mixerless-module bypass amplifiers (--status for bypass state) |
souk-find-lnas |
Discover populated LNA bias monitors (--status for voltage/current readings) |
souk-rf-peripherals-status |
Combined attenuator, bypass-amplifier, and LNA status report |
| Document | Description |
|---|---|
| Installation Guide | Full client and server installation, SD card setup, daemon configuration |
| Getting Started | Configuration, usage guide, and worked examples for all features |
| Fast Frequency Modulation | Real-time IQ → frequency/dissipation conversion and resonator tracking via per-tone probe-point modulation |
| Calibration | Power calibration model and RF signal-chain configuration |
| Tone Power Notes | Dynamic-range, VACC, crest-factor, and blind-tone power guidance |
| Resonator Noise Workflow | Drive-power tuning, blackbody-load directory layout, and on/off-resonance noise captures |
| Parameter-Series Measurements | Repeating any measurement across an external parameter (attenuation, temperature, time, ...) with run directories, resume, and live summaries |
| RF Peripherals | RF attenuator, bypass amplifier, and discovery tooling |
| LNA Bias | Cryostat LNA bias control, soft-off, and status monitoring |
| Dual Pipeline | Dual-pipeline setup, initialisation model, and multi-server operation |
| Clock Source | PL reference clock selection (internal/external) and PLL status |
| Timing and PTP | ptp4l, chrony, timing-monitor setup, standalone monitoring, and firmware-sync readiness |
| Timing Site Checklist | Site commissioning checks for PTP GM, chrony, monitor state, and holdover behaviour |
| TSU Strobe and Timed Sync | 1-PPS TSU strobe, firmware timed-sync bring-up for multi-board timestamp alignment, and test scripts |
| v7.9 Multitone Notes | VACC multitone design notes and constraints |
| Changelog | Version history, feature list, and roadmap |
YAML-based configuration with five sections: rfsoc_host, firmware, rf_frontend, cryostat, and detector. A template config with all parameters and defaults is bundled with the package. Config files live wherever you choose on the client side - keep them with your measurement data.
The standard workflow is: create or pull a config, edit hardware-/firmware-specific settings, connect with it, and use push_config() / pull_config() to synchronise with the server. Calibration files referenced in the config are transferred automatically.
See Getting Started - Configuration for details.
- Client: Python >= 3.10. Tested on Linux (Python 3.10, 3.12) and Windows (Python 3.12). Dependencies: numpy, scipy, matplotlib, pyyaml, ipython, pyqt5.
- Server: Python >= 3.10 on the Xilinx/RFSoC CASPER Linux image. Requires
souk_mkid_readoutfrom the souk-firmware repository.