Blackchirp 2.0.0

Blackchirp 2.0.0 is a major release that replaces the qmake build with CMake and ships official binary packages, replaces compile-time hardware selection with a runtime configuration model, introduces Python hardware drivers as a first-class implementation type, rebuilds the acquisition pipeline around a lock-free ring buffer, and overhauls logging, configuration, and the on-disk hardware identifier scheme. A v1.x user upgrading to 2.0 should also read the migration guide, which walks through the upgrade actions for each user-visible change below.

Highlights

  • Replaced the qmake build with CMake and added official binary packages for Linux, Windows, and macOS, so most users no longer need to build from source. See Installation.

  • Replaced compile-time hardware selection with a runtime configuration model: a single binary supports any combination of compatible hardware, organized into profiles, loadouts, and FTMW presets selectable from the Hardware menu. See Hardware and Library Configuration.

  • Added Python hardware drivers as a first-class implementation type alongside the C++ drivers, with hot reload, custom protocol support, and per-type capability declarations. See Python Hardware.

  • Rebuilt the FTMW acquisition pipeline around a single-producer single-consumer ring buffer with parallel batched parse and accumulate, raising sustained shot rates and reducing dropped frames under load. See Viewing FTMW Data.

  • Consolidated logging through a thread-safe LogHandler and replaced qDebug() everywhere with the bcLog / bcWarn / bcError / bcDebug and hwLog / hwWarn / hwError / hwDebug free-function families; a runtime debug-logging toggle in the new Application Configuration dialog writes a debug_YYYYMM.csv file when enabled. See Application Log and Application Configuration.

  • Added an in-process crash handler that captures POSIX signals (Linux, macOS) and Windows unhandled exceptions to a diagnostic report under <savePath>/log/crashes/, with a startup notification when an earlier run crashed. Release builds keep debug info so the captured frames resolve to source lines via addr2line / atos / WinDbg. See Crash Reports.

  • Overhauled the documentation: refreshed the User Guide end to end against the 2.0 feature set, added the new Developer Guide for contributors, and substantially expanded the C++ API reference to cover the hardware, experiment, persistence, and orchestration classes.

  • Refreshed the companion blackchirp Python package against the v2 on-disk schema, added LIF read and processing support (BCLIF, BCLifTrace), extended BCFTMW with a differential-FID API and configurable axis units, hardened the reader against schema drift across v1 and v2 fixtures, and restructured the Sphinx pages into a per-class layout. Tagged on PyPI as 0.1.0rc1. See Python Module.

Build and distribution

  • Replaced the qmake build with CMake. config.pri is no longer used; build options are configured through CMake cache variables. See Installation.

  • Added official binary packages for Linux, Windows, and macOS, with bundled Qt redistributables on Windows and macOS. See Installation.

  • Added cross-platform CPack packaging assets and a GitHub Actions release workflow that produces release artifacts for every supported platform.

  • Added a user-configurable hardware-selection system at configure time (BC_ALLHARDWARE and per-driver toggles) so packagers can produce restricted builds when needed. See Installation.

  • Declared Eigen3 as an explicit CMake dependency.

  • Added a CMake documentation build (Sphinx + Doxygen) with dual-Doxyfile support for ReadTheDocs.

  • Replaced the static virtualdata.qrc resource with a dynamic FID simulation generator, so the Virtual digitizer produces synthetic data without bundling sample CSVs. See First Run.

Hardware configuration

  • Added the runtime hardware configuration model with profiles, loadouts, and FTMW presets. Hardware is enabled and configured from the Runtime Hardware Configuration dialog without recompilation. See Hardware Profiles, Loadouts, and FTMW Presets.

  • Added a first-run hardware onboarding flow that walks a new user through profile creation. See First Run.

  • Added the Hardware Loadouts submenu and a loadout selector in the runtime hardware configuration dialog. See Hardware Menu.

  • Added the FTMW preset bar with full create, copy, save-as, and delete operations. See FTMW Presets.

  • Added the consolidated FTMW Configuration menu entry and FtmwConfigDialog, replacing the v1.x scattered FTMW pages. See FTMW Configuration.

  • Added the unified Application Configuration dialog (font, save path, runtime LIF toggle, debug-logging toggle), replacing the per-setting menu entries from v1.x. See Application Configuration.

  • Added a VendorLibrary staging system and a Library Configuration interface for managing externally-supplied drivers, with platform-specific installation guidance. See Library Status and Installation.

  • Added the hardware settings registry: per-device settings are declared once and rendered into the per-device dialog by the HwSettingsWidget with priority-grouped layout. All hardware classes (flow controllers, pressure controllers, temperature controllers, IO boards, GPIB controllers, LIF lasers and digitizers, FTMW digitizers, clocks, chirp sources, and pulse generators) were migrated onto the registry. See Hardware Dialog.

  • Added per-object hardware threading configuration to the hardware profile. See Hardware Profiles.

  • Added a “Direct Control” hardware concept for hardware operated outside an experiment. See Hardware and Library Configuration.

Hardware drivers

  • Added Python hardware drivers backed by a QProcess / JSON-IPC bridge, with hot reload from the per-device dialog and per-type trampolines for AWGs, flow controllers, IO boards, clocks, pressure controllers, temperature controllers, FTMW digitizers, LIF digitizers, pulse generators, GPIB controllers, and LIF lasers. See Python Hardware.

  • Python drivers expose RS232, TCP, GPIB, and Custom protocols. See Per-Type Capabilities.

  • The Python hardware combo box prepopulates from class names scanned out of available scripts. See Selecting a Python Driver.

  • Added runtime communication-protocol selection per hardware object via the per-device dialog, with protocol-specific widgets backed by group-based settings storage. See Hardware Dialog.

  • Modernized the GPIB controller architecture so multiple GPIB controllers (mixed LAN and RS232) cohabitate in one installation. See Hardware Details.

  • Added Test Connection and Communication Settings shortcuts to the per-device dialog. See Hardware Dialog.

  • Replaced the four fixed AWG protection/gate spinboxes with a generalized marker system: arbitrary MarkerChannel / MarkerRole definitions are entered through a marker table on the chirp configuration tab. See Markers Tab on the chirp setup page.

  • Added the LabJack vendor-library wrapper with platform- specific backends (Linux/macOS exodriver, Windows UD), so LabJack support no longer requires the exodriver at compile time. See Library Status and Installation.

  • Added the Spectrum vendor-library wrapper with dynamic loading for both Spectrum digitizer families. See Library Status.

Acquisition and data flow

  • Replaced the per-shot Qt signal waveform path with a single-producer single-consumer ring buffer (WaveformBuffer) and a worker-thread parse/accumulate stage. See Viewing FTMW Data.

  • Added backpressure-triggered pre-accumulation in FtmwDigitizer so the worker thread keeps up under bursty load. See Viewing FTMW Data.

  • Added parallel batched parse and accumulate in AcquisitionManager. See Viewing FTMW Data.

  • Consolidated the FTMW experiment-setup pages into FtmwConfigWidget. See Experiment Setup.

  • Extracted the LO scan and DR scan controls into reusable widgets and incorporated them into the experiment-wizard Type page. See FTMW Experiment Setup.

  • The number-of-chirps spinbox is now enabled unconditionally; a warning is emitted when the active digitizer does not support multi-record acquisition. See Digitizer Setup.

User interface

  • Added a Help menu with About dialog, library information, and links to online resources. See User Interface Overview.

  • Added the FTMW preset submenu under the Hardware menu. See Hardware Menu.

  • Reworked the experiment-info panel and wired HwStatusBox configure actions so each status box opens the device dialog directly. See User Interface Overview.

  • Polished the left-side status panel layout and shortened hardware status box titles. See User Interface Overview.

  • Added a Fixed step mode and full-precision handling to the ScientificSpinBox. See User Interface Overview.

  • Added a Save-As workflow on the FTMW preset bar that copies the current configuration into a new preset, with drift-detection on Save. See Hardware Menu.

  • Updated the Peak Find Widget icon. See Viewing FTMW Data.

  • Added an Appearance button to the Peak Find toolbar that opens the FT peak-marker styling editor directly, without hunting for the curve in the main-plot right-click menu. See Viewing FTMW Data.

  • Added a Peak Find display filter: a toggleable 2x2 grid filters the peak table by frequency and intensity range, plus a separate “In view” toggle that limits the table to peaks within the main FT plot’s visible range. These filters affect only the displayed table, not the search bounds or the peaks drawn on the plot. See Viewing FTMW Data.

  • Added peak navigation to the Peak Find panel: double-click a row to center the main FT plot on that peak, or use the row’s “Center on” context menu to frame any FT plot. With the table focused, Enter re-centers the current peak, Up/Down step through the visible rows, and Left/Right step to the next lower/higher frequency peak across the full peak list (regardless of filtering or sort order), centering the main plot on each. The frequency window half-width is configurable in the Peak Finding Options dialog. See Viewing FTMW Data.

  • The Peak Find search parameters (frequency range, SNR, window size, polynomial order, and navigation half-width) are now remembered per experiment, stored in a peakfind.csv file and falling back to the application-wide defaults when none has been saved, mirroring the FID processing-settings behavior. See Viewing FTMW Data.

  • Added a selectable column delimiter for curve XY exports: a drop-down beside the right-click Export XY action chooses semicolon, comma, tab, or aligned whitespace. The choice is persisted application-wide and shared between Blackchirp and the viewer. See Plot Controls.

  • Relaxed the experiment-setup dialog minimum size. See Experiment Setup.

  • Streamlined the overlay creation and configuration dialog: flattened the nested group boxes into aligned settings tables and moved the validity and progress indicators into the dialog button bar. See Overlays.

Logging and diagnostics

  • Redesigned LogHandler as a thread-safe global singleton with a QAnyStringView API. See Application Log.

  • Added bcLog / bcWarn / bcError / bcDebug and hwLog / hwWarn / hwError / hwDebug free functions and migrated every call site in the source tree off logMessage signals and qDebug(). See Application Log.

  • Added a runtime debug-logging toggle in the Application Configuration dialog; when enabled, debug-level output is written to a debug_YYYYMM.csv file alongside the standard log. See Application Configuration.

  • Successful connection-test events are highlighted in the Log tab so they stand out from informational chatter. See Application Log.

  • Added an in-process crash handler. POSIX installs sigaction handlers (SIGSEGV/SIGABRT/SIGFPE/SIGILL/SIGBUS) on a sigaltstack and writes a text crash log via raw write(2) with frames resolved through dladdr to module(+0xoffset) [0xpc] lines. Windows installs SetUnhandledExceptionFilter plus the C-runtime auxiliaries and emits a minidump (MiniDumpWriteDump) alongside a matching text sidecar so triage tooling sees the same header on both platforms. Crash artifacts land under <savePath>/log/crashes/ and carry the build identity (git SHA) so they can be resolved against an unstripped binary. See Crash Reports and Crash Handling and Triage (68449b0b).

  • Added a startup notification dialog when prior crash reports are present. The most recent acknowledged timestamp is persisted under the dialog’s settings group, so a dismissed report does not re-prompt unless a still-newer crash arrives. See Crash Reports (68449b0b).

  • Release builds now keep debug info (-g / /Zi) so the crash-handler stacktraces resolve against the unstripped binary. Stripping continues to happen at install time in cmake/Packaging.cmake (68449b0b).

File formats and data storage

  • Introduced QSettings versioning: each major version stores its configuration under its own QSettings namespace (Blackchirp2, Blackchirp3, …), so v1.x and v2.x do not share state. See Application Configuration.

  • Migrated to label-based hardware identification across configurations and on-disk storage. Implementation key strings became labels keyed off the runtime hardware configuration. See Data Storage.

  • Added a markers.csv file written under each experiment directory recording the active marker channel definitions for the run. See Data Storage.

  • Added the RfConfigSnapshot helper for serializing the full RF configuration with each experiment. See RF Configuration.

Python companion module

  • Hardened BCFTMW and BCFid against schema drift across the v1 and v2 on-disk formats: window-function dispatch is driven by an explicit name → scipy.signal map, sideband selection accepts both numeric and string forms, FtUnits accepts both numeric and FtV / FtmV / FtuV / FtnV forms, and a single helper centralizes the dual-form parse used by every enum-bearing field. Unknown enum values raise ValueError rather than falling through to a silent default. See BCFid and BCFTMW (10bd9d08).

  • BCExperiment reads markers.csv when present and exposes the active marker channel definitions as a markers DataFrame attribute. The hardware.csv reader accepts both the legacy subKey and the current driver second-column headers, and tolerates the historical hardwareType column being numeric, string, or absent — so v1 fixtures continue to load alongside v2 captures. See BCExperiment (10bd9d08).

  • Added a differential-FID API on BCFTMW: is_multi_segment, num_backups, and get_differential_fid(start, end) produce a BCFid whose data and shot count reflect the cumulative FID between two backup points in fidparams.csv, with vmult scaling recomputed from the bounding entries. See BCFTMW (10bd9d08).

  • Added optional axis-unit selection on BCFid.x, BCFid.xy, BCFid.ft, and BCFTMW.process_sideband. Time axes accept s / ms / us / μs / ns and frequency axes accept Hz / kHz / MHz / GHz / THz; intensities and the user-facing μs/MHz gate keyword arguments are unchanged. See BCFid (9285f0f8).

  • Added BCLIF and BCLifTrace for LIF read and processing, mirroring the C++ LifTrace semantics. BCLifTrace exposes per-trace smooth (IIR low-pass plus Savitzky–Golay) and integrate (sample-space trapezoidal sum, with reference-channel ratio when a reference is present); BCLIF provides delay_axis / laser_axis, delay_slice / laser_slice, and image aggregating helpers with configurable fill=np.nan|0.0 for missing scan points. See BCLIF and BCLifTrace (dc7e7640).

  • Restructured the Sphinx documentation into per-class pages matching the C++ API reference layout, refreshed single-fid.ipynb against the v2 schema, and added a fully executed single-lif.ipynb covering trace loading, smoothing, integration, slice extraction, and 2-D image rendering. See Python Module (7cb8ab84).

  • Added multi-experiment FID coaveraging via the new coaverage_fids and coaverage_spectra module-level functions. coaverage_fids produces a BCFid whose raw integer data is the sample-by-sample sum of the inputs and whose shot count is the sum of input shot counts, with optional cross-correlation phase correction against a chosen reference window. coaverage_spectra returns a shot-weighted magnitude-spectrum coaverage. Both refuse on any mismatch in spacing, size, sideband, probefreq, vmult, or frame count. See BCFid.

  • Tagged the package as 0.1.0rc1 on PyPI. pip install blackchirp continues to resolve to the prior stable release; testers opt in with pip install --pre blackchirp or pip install blackchirp==0.1.0rc1 (4a9b69d0).

Bug fixes

Hardware.

  • Fixed inverted marker bit-packing on the AWG70002A and AWG7122B (ba07d008).

  • Fixed common-LO clock persistence and clock-frequency signal chaining (836b7750).

  • Fixed the Apply Clock Settings button so clock changes can be pushed without closing the dialog (cd07dd5f).

  • Fixed aux-data registration key mismatches in the flow controller and temperature controller paths and corrected the resulting plot labels (79dfe7e8, 8e67bebb, 8458aad9).

  • Fixed flow-controller channel display and the underlying destructor; refined the gas-flow display layout (e53d7f90, eb8d7fbc, 6783af97).

  • Fixed the flow-controller poll loop and the Python template- copy workflow (63fee23d).

  • Fixed Python hardware experiment aux-data emission and host- script deployment (dfb57021).

  • Fixed threaded hardware destructors not running on shutdown: ~HardwareManager joined each worker thread but never deleted the moved-to-thread object, so settings persisted by ~SettingsStorage::save() (such as flow-controller channel names on PythonFlowController) were silently dropped on exit (e0acf07d).

  • Fixed a destruction race during application close when a Python hardware driver was actively polling: PythonProcess::sendRequest ran a nested QEventLoop on the hardware thread to service relay requests, and shutdown could destroy the driver while that loop was in flight, so the post-loop isRunning() check dereferenced a dangling pointer and the process segfaulted out of the FlowController poll path. Quiesce poll timers via a new HardwareObject::prepareForShutdown() virtual invoked before worker-thread teardown, with a QPointer guard around the nested loop for the residual case (70fdc9ad).

  • Fixed the flow-controller poll interval not refreshing when the user changed it in the Hardware Settings dialog: the PythonFlowController trampoline overrode readSettings without chaining to FlowController::readSettings, so the base-class timer reload was silently bypassed. Restructured the contract as a non-virtual interface — HardwareObject exposes hwReadSettings as the sole entry point, each intermediate hardware base final-overrides it and dispatches to a per-base hook (fcReadSettings, pcReadSettings, tcReadSettings, pgReadSettings, awgReadSettings, clockReadSettings, ftmwReadSettings, gpibReadSettings, ioReadSettings, lifLaserReadSettings, lifDigitizerReadSettings) — so a derived driver that forgets to chain becomes a compile error rather than a silent runtime omission (d512ee5e).

Hardware configuration.

  • Fixed loadout persistence and timestamp handling on save (1c9586b2, f8e83611).

  • Fixed the SettingsStorage nested-group wipe that affected FTMW preset CRUD (3d73f08d).

  • Fixed several runtime-hardware-config defects: protocol registry initialization, vtable crashes during hardware destruction, and a deadlock in the configuration dialog (68e49ab2).

  • Fixed multiple Runtime Hardware Configuration Dialog defects (7a299892, aced0817, ed915a93, d66aa076).

  • Fixed BC_ALLHARDWARE build errors and the LIF logic under BC_ALLHARDWARE (0bfe75c0, fe7b5f0a).

  • Fixed GPIB protocol configuration and connection testing (cfd85d62).

  • Fixed Communication Settings dialog and clock-manager initialization (19f60cf6, d733af0d).

  • Fixed vendor-library search priority and the library browse dialog (1c4f6604).

  • Fixed QVariant enum serialization and duplicate error messages when switching protocols (f7e3d6f7).

  • Fixed optional-hardware configuration init and wizard page population (be8a82e2).

Acquisition.

  • Fixed chirp recalculation and RF-config loss in the experiment wizard (1483a2d7).

  • Fixed a minor FtmwViewWidget rendering glitch under heavy processing load (637d6d4e).

  • Fixed a long-standing crash on LIF-only experiments when the backup interval was set to a non-zero value: the backup scheduler dereferenced the unallocated FTMW configuration. LIF data is segment-flushed at acquisition time, so backup is now skipped for non-FTMW experiments (f335b11d).

Tooling.

  • Fixed an intermittent crash on closing blackchirp-viewer (446a5c0a).

  • Made ZoomPanPlot curve attach and detach thread-safe via a curve registry, eliminating a class of races that surfaced in both the main application and the viewer (af4aab85).

  • Fixed additional ZoomPanPlot defects surfaced during the API-reference work (feb21753).

User interface.

  • Fixed ScientificSpinBox not displaying an initial value of 0.0 (00b0608d).

  • Fixed overlays and aux data never persisting for experiments opened by path: the disk-loading Experiment constructor passed the placeholder number rather than the header-resolved one to its sub-storages, and OverlayStorage gates all disk writes on a valid experiment number (5a595db8).

  • Fixed catalog overlays always rendering as a step plot and overwriting the saved curve style on every load. The stick-plot style is now a creation-time default, so switching a convolved catalog to a line plot survives reloads (1f473d60).

  • Fixed the overlay Configure dialog silently erasing an existing comment because the comment field was never populated from the overlay before the dialog wrote it back (286fe2fe).

  • Fixed a stray duplicate checkbox appearing in the overlay table when toggling visibility: the paint-based delegate also created a real checkbox editor (175e66b3).

  • Fixed a “pure virtual method called” crash when removing an overlay while a plot filter pass was in flight; the curve is now drained and unregistered before destruction (b82736e6).

  • Fixed the Peak Find Remove control doing nothing: the button’s clicked signal was never connected, so selected peaks could not be removed from the list (004c523b).

  • Fixed a use-after-free when a Peak Find pass outlived its widget. The background search discarded its future, so reloading the experiment or tearing down the dock mid-search could free members still in use; the pass is now drained before destruction (9428b017).

  • Fixed Peak List Export dialog defects: the dipole spin box ignored its enabled setting on load, persisted fractional intensity thresholds were truncated to integers, cancelling the save-file dialog raised a spurious export-failure box, and edits to the shots/intensity table were discarded unless the FTB format was exported (f15eb918).

  • Fixed the catalog overlay creation dialog leaving the frequency-range filtering controls disabled even after a valid catalog file was selected; they were only editable when reconfiguring an existing overlay (3341b0ad).

  • Fixed the Generic XY overlay dialog locking the delimiter, header-line, column-mapping, and filtering controls until the file already parsed, so a file that needed manual settings to parse could never be coaxed into parsing. Source-file validity is now existence-based; parse correctness still gates overlay creation (3341b0ad).

  • Fixed the Generic XY overlay reporting an opaque parser-availability or parse failure. The selected file is always handed to the Generic XY parser, and the failure message states the cause, such as a file containing only one column (3341b0ad).

Python companion module.

  • Made BCFid.ft idempotent across all processing options. The windowed slice taken from self.data was a numpy view, so the in-place FidRemoveDC subtraction and the per-frame exp-decay multiplication mutated self.data and a second ft call on the same FID produced different output (6fed7a0e).