FtWorker

FtWorker is the analysis-pipeline class that converts Fid objects into Ft magnitude spectra. Three slot methods cover the three usage patterns: doFT() for a single FT, doFtDiff() for the difference between two spectra, and processSideband() for the incremental LO-scan stitching that builds a broadband spectrum from a series of per-LO-step FTs. Each slot is invoked through QtConcurrent::run rather than via a dedicated QThread; results are returned both by signal (ftDone, ftDiffDone, sidebandDone) and as the slot’s return value, so the same instance serves both the asynchronous UI path (FtmwViewWidget) and the synchronous batch path (BatchManager).

The transform itself uses the GSL mixed-radix real FFT, which is most efficient when the FID length factors into powers of 2, 3, and 5. The frequency-domain plot, peak finder, and overlay system in both blackchirp and the blackchirp-viewer consume the resulting Ft via ExperimentViewWidget; the user-facing controls that drive FidProcessingSettings are described in Data Storage and the FT-domain UI documentation.

Preprocessing pipeline

Before the FFT, FtWorker applies an optional pipeline of time-domain steps driven by FidProcessingSettings:

  • Truncation to a sub-window via startUs / endUs.

  • DC removal (subtract the FID mean).

  • Exponential apodization (expFilter).

  • Zero-padding (zeroPadFactor).

  • Window-function apodization — one of FtWindowFunction: Bartlett, Blackman, Blackman-Harris, Hamming, Hanning, Kaiser-Bessel.

The magnitude scale of the output Ft is controlled by FidProcessingSettings::units. The enumeration values map directly to powers of ten relative to the digitized voltage: e.g. FtmV = 3 multiplies by 10³ / rawSize, so the spectrum reads in mV per FFT bin. The window-function cache is keyed on (window, length) and guarded by pu_winfLock so repeated FFTs at the same size pay the window cost only once.

Threading

FtWorker is a QObject but is not moved to a dedicated QThread. Callers invoke doFT(), doFtDiff(), and processSideband() through QtConcurrent::run, which schedules each call on the global thread pool. The signals ftDone, fidDone, ftDiffDone, and sidebandDone are emitted from the thread-pool thread, so connections to UI slots require queued delivery (the default when the receiver lives on a different thread). When called from a non-UI thread (e.g. a BatchManager), the return values of doFT() and filterFid() can be used directly without waiting for the signals. The id parameter of doFT() selects the path: pass id >= 0 to get signal delivery (asynchronous) or id = -1 to suppress signals (synchronous).

GSL workspace allocation is guarded by pu_fftLock and spline allocation by pu_splineLock, so the same FtWorker instance can be called concurrently from multiple thread-pool threads provided each concurrent call uses a different code path.

Resource management

GSL wavetables, workspaces, and spline objects are allocated lazily on first use and freed by the destructor. When idle-cleanup is enabled via setIdleCleanupEnabled(), resources are also freed automatically after a 5-minute inactivity timeout (cleanupResources()). Call resetIdleTimer() after each processing operation to restart the countdown.

API Reference

class FtWorker : public QObject

Processes Fid objects into magnitude spectra using the GNU Scientific Library FFT.

Not moved to a dedicated QThread: callers invoke doFT(), doFtDiff(), and processSideband() through QtConcurrent::run, so the ftDone / fidDone / ftDiffDone / sidebandDone signals are emitted on a thread-pool thread and require queued connections to UI slots. The id parameter of doFT() picks the path: id = -1 suppresses signals (synchronous use); id >= 0 emits async signals. Per-instance locks (pu_fftLock, pu_splineLock, pu_winfLock) allow concurrent calls on the same instance provided each call uses a different code path.

GSL wavetables, workspaces, and spline objects are allocated lazily on first use and freed by the destructor. When idle-cleanup is enabled via setIdleCleanupEnabled(), resources are also freed after a 5-minute inactivity timeout; resetIdleTimer() restarts the countdown after each processing operation.

See also

Fid, Ft

Public Types

enum FtUnits

Output magnitude units for the Fourier-transform result.

The enumeration value is the base-10 exponent applied to the raw GSL magnitude before dividing by the FID length. For example, FtmV = 3 means the output is in millivolts.

Values:

enumerator FtV

Volts.

enumerator FtmV

Millivolts.

enumerator FtuV

Microvolts.

enumerator FtnV

Nanovolts.

enum FtWindowFunction

Window functions applied to the FID before the FFT.

Windowing reduces spectral leakage at the cost of reduced frequency resolution. None applies a rectangular (uniform) window.

Values:

enumerator None

No windowing (rectangular window).

enumerator Bartlett

Bartlett (triangular) window.

enumerator Blackman

Blackman window.

enumerator BlackmanHarris

Blackman-Harris window.

enumerator Hamming

Hamming window.

enumerator Hanning

Hanning (Hann) window.

enumerator KaiserBessel

Kaiser-Bessel window with \(\beta = 14\).

enum DeconvolutionMethod

Method used to combine sideband FT magnitudes during LO-scan co-averaging.

Values:

enumerator Harmonic_Mean

Combine magnitudes using the shot-weighted harmonic mean.

enumerator Geometric_Mean

Combine magnitudes using the shot-weighted geometric mean.

Public Functions

explicit FtWorker(QObject *parent = nullptr)

Constructor; initializes GSL pointer members to null and creates internal locks and idle timer.

Parameters:

parent – Optional parent object.

~FtWorker()

Destructor; frees all GSL wavetables, workspaces, and spline objects.

Public Slots

Ft doFT(const FidList fl, const FtWorker::FidProcessingSettings &settings, int frame = 0, int id = -1, bool doubleSideband = false)

Filters and Fourier-transforms a list of FIDs.

Selects the FID at frame (or averages all FIDs when frame < 0), applies the preprocessing pipeline described by settings, then computes the mixed-radix GSL FFT and converts coefficients to a magnitude spectrum.

The magnitude scale factor is 10^(settings.units) / rawFidSize. The DC bin at the probe frequency is zeroed regardless of settings.

When id >= 0, fidDone() and ftDone() signals are emitted (useful for asynchronous callers in the UI thread). When id == -1 the signals are suppressed and only the return value is used (useful for synchronous callers such as BatchManager).

GSL workspace and wavetable are allocated or reallocated when the FID length changes; allocation is protected by pu_fftLock.

Parameters:
  • fl – List of FIDs to process.

  • settings – Preprocessing and FFT parameters.

  • frame – Frame index to process; negative values average all frames.

  • id – Caller-supplied identifier forwarded to ftDone() and fidDone(); -1 suppresses signals.

  • doubleSideband – When true, produce a symmetric double-sideband spectrum.

Returns:

The completed magnitude spectrum, or a default-constructed Ft if fl is empty.

void doFtDiff(const FidList refList, const FidList diffList, int refFrame, int diffFrame, const FtWorker::FidProcessingSettings &settings)

Computes the difference spectrum between two FID lists and emits ftDiffDone().

Each FID in refList and diffList must have matching length, sideband, and spacing. When the two spectra share the same LO frequency, the difference is computed bin-by-bin. When the LO frequencies differ, the diffList spectrum is resampled onto the refList frequency grid using cubic spline interpolation before subtraction.

Parameters:
  • refList – Reference FID list.

  • diffList – Comparison FID list.

  • refFrame – Frame index for the reference.

  • diffFrame – Frame index for the comparison.

  • settings – Preprocessing and FFT parameters applied to both lists.

void processSideband(const SidebandProcessingData &d, const FidProcessingSettings &settings)

Processes one step of an LO-scan sideband co-average and emits sidebandDone() on the last step.

Call this slot once per LO frequency step with monotonically increasing SidebandProcessingData::currentIndex values from 0 to SidebandProcessingData::totalFids - 1. The internal LoScanData accumulator is reset when currentIndex == 0. On the final step, the accumulated spectrum is emitted via sidebandDone().

Individual FT bins are combined using the method specified by SidebandProcessingData::dcMethod (harmonic or geometric mean, weighted by shot count). Fractional grid alignment between LO steps is resolved by linear interpolation of the nearest two bins.

Parameters:
  • d – Processing parameters and FID data for this LO step.

  • settings – Preprocessing and FFT parameters.

FilterResult filterFid(const Fid fid, const FtWorker::FidProcessingSettings &settings)

Applies the preprocessing pipeline to a single FID without computing an FFT.

Performs truncation, DC removal, exponential apodization, window function application, and zero-padding as specified by settings. This is called internally by doFT() and can also be used independently to obtain a filtered FID for display.

Parameters:
  • fid – Source FID.

  • settings – Preprocessing parameters.

Returns:

FilterResult containing the preprocessed floating-point samples and their min/max.

void cleanupResources()

Frees all GSL wavetables, workspaces, spline objects, and window-function cache.

Safe to call from any thread; each resource group is protected by its own read/write lock. Also stops the idle timer. After this call, the next doFT() invocation will reallocate all necessary GSL resources.

void resetIdleTimer()

Restarts the idle-cleanup countdown timer.

Has no effect when idle cleanup is disabled or no resources are currently allocated. The timer is restarted via a queued QMetaObject::invokeMethod call to ensure it runs on the correct thread.

void setIdleCleanupEnabled(bool enabled)

Enables or disables automatic resource cleanup on idle timeout.

When enabled is false, the idle timer is stopped immediately. When enabled is true, the timer will fire after 5 minutes of inactivity and call cleanupResources().

Parameters:

enabledtrue to enable idle cleanup, false to disable.

Signals

void ftDone(Ft ft, int id)

Emitted when a doFT() call completes.

Only emitted when doFT() is called with id >= 0. The id value is forwarded from the doFT() call so the receiver can match results to requests.

Parameters:
  • ft – The completed magnitude spectrum.

  • id – Caller-supplied identifier passed to doFT().

void fidDone(QVector<double> fid, double spacing, double min, double max, quint64 shots, int id)

Emitted after FID filtering is complete, carrying the preprocessed FID for display.

Only emitted when doFT() is called with id >= 0.

Parameters:
  • fid – Preprocessed floating-point FID samples (after windowing/truncation/DC removal).

  • spacing – Inter-sample spacing in microseconds.

  • min – Minimum sample value after preprocessing.

  • max – Maximum sample value after preprocessing.

  • shots – Number of co-averaged shots.

  • id – Caller-supplied identifier passed to doFT().

void ftDiffDone(Ft ft)

Emitted when a doFtDiff() call completes.

Parameters:

ft – Difference spectrum (reference FT minus comparison FT).

void sidebandDone(Ft ft)

Emitted when the final step of processSideband() completes.

Parameters:

ft – The fully co-averaged LO-scan sideband spectrum.

Private Functions

QPair<QVector<double>, double> resample(double f0, double spacing, const Ft ft)

Resamples the frequency axis of ft onto a grid starting at f0 with spacing spacing.

Uses a GSL cubic spline for interpolation. Points outside the valid range of ft are set to zero. Spline allocation is guarded by pu_splineLock.

Parameters:
  • f0 – Starting frequency of the target grid (MHz).

  • spacing – Target frequency spacing (MHz).

  • ft – Source spectrum to resample.

Returns:

Pair of (resampled magnitude vector, actual starting frequency of the output grid in MHz).

void makeWinf(int n, FtWindowFunction f)

Recomputes d_winf for length n and function f.

void winBartlett(int n)

Fills d_winf with Bartlett coefficients.

void winBlackman(int n)

Fills d_winf with Blackman coefficients.

void winBlackmanHarris(int n)

Fills d_winf with Blackman-Harris coefficients.

void winHamming(int n)

Fills d_winf with Hamming coefficients.

void winHanning(int n)

Fills d_winf with Hanning coefficients.

void winKaiserBessel(int n, double beta)

Fills d_winf with Kaiser-Bessel coefficients for parameter beta.

void clearSplineMemory()

Frees the GSL spline and accelerator objects under pu_splineLock.

Private Members

std::unique_ptr<QReadWriteLock> pu_fftLock

Guards GSL FFT wavetable and workspace allocation.

std::unique_ptr<QReadWriteLock> pu_splineLock

Guards GSL spline and accelerator allocation.

std::unique_ptr<QReadWriteLock> pu_winfLock

Guards the cached window-function vector.

gsl_fft_real_wavetable *real

Wavetable for GNU Scientific Library FFT operations.

gsl_fft_real_workspace *work

Workspace for GNU Scientific Library FFT operations.

int d_numPnts

FID length for which the current wavetable and workspace were allocated.

gsl_spline *p_spline

GSL cubic spline object used for cross-LO FT resampling.

gsl_interp_accel *p_accel

GSL interpolation accelerator associated with p_spline.

int d_numSplinePoints

Number of points for which p_spline was allocated.

FtWindowFunction d_lastWinf = {None}

Window function used to compute the cached d_winf vector.

int d_lastWinSize = {0}

Size for which d_winf was last computed.

struct FtWorker::LoScanData d_loScanData
QVector<double> d_winf

Cached window-function coefficients; recomputed when size or type changes.

std::unique_ptr<QTimer> pu_idleTimer

Single-shot timer that fires after 5 minutes of inactivity.

bool d_resourcesAllocated = {false}

true when any GSL resource has been allocated since the last cleanup.

bool d_idleCleanupEnabled = {false}

true when the idle-cleanup mechanism is active.

Private Slots

void onIdleTimeout()

Triggered by the idle timer; calls cleanupResources() when idle cleanup is enabled.

void startIdleTimer()

Starts or restarts the idle timer; must be called from the object’s thread.

Friends

friend class FtWorkerTest
struct FidProcessingSettings

Parameters that control FID preprocessing and FFT output.

All fields must be set before passing an instance to doFT() or filterFid().

Public Members

double startUs

Start of the time window to process, in microseconds (0 = beginning of FID).

double endUs

End of the time window to process, in microseconds (0 = end of FID).

double expFilter

Exponential apodization time constant in microseconds (0 = disabled).

int zeroPadFactor

Zero-padding multiplier: 0 = none, 1 = next power of 2 × 2, 2 = × 4.

bool removeDC

When true, subtract the mean of the windowed FID before the FFT.

FtUnits units

Output magnitude units; see FtUnits.

double autoScaleIgnoreMHz

Frequency half-width around the LO (MHz) to exclude from autoscale min/max tracking.

FtWindowFunction windowFunction

Apodization window applied after truncation and DC removal.

struct FilterResult

Return value of filterFid().

Public Members

QVector<double> fid

Preprocessed (floating-point) FID samples ready for FFT input.

double min = {0.0}

Minimum sample value after all preprocessing steps.

double max = {0.0}

Maximum sample value after all preprocessing steps.

struct LoScanData

Per-instance accumulator for an in-progress LO-scan sideband co-average.

Populated during successive processSideband() calls; reset when SidebandProcessingData::currentIndex == 0.

Public Functions

inline uint indexOf(const double f)

Returns the output grid index nearest to frequency f.

inline double frequency(int index)

Returns the frequency corresponding to output grid index index.

inline double relDistance(double f)

Returns the fractional grid offset of f relative to its nearest grid point (in units of ftSpacing).

Public Members

QVector<double> ftData

Accumulated magnitude values on the output frequency grid.

uint ftPoints = {0}

Number of output frequency grid points.

double ftSpacing = {0.0}

Output frequency grid spacing in MHz.

quint64 totalShots = {0}

Total co-averaged shots accumulated so far.

std::pair<double, double> ftXRange = {0.0, 0.0}

[start, end] frequency range of the output grid (MHz).

QVector<quint64> counts

Per-bin shot-count accumulator used for weighted averaging.

struct SidebandProcessingData

Input bundle for a single step of an LO-scan sideband co-average.

processSideband() is called once per LO frequency step. When currentIndex reaches totalFids - 1, the accumulated result is emitted via sidebandDone().

Public Members

FidList fl

FID list for this LO step.

int frame = {0}

Frame index within fl to process (-1 = average all).

int totalFids = {0}

Total number of LO-frequency steps in the full scan.

int currentIndex = {0}

Zero-based index of this step within the scan.

double minOffset = {-1.0}

Minimum frequency offset from the LO to include (MHz).

double maxOffset = {-1.0}

Maximum frequency offset from the LO to include (MHz).

std::pair<double, double> loRange = {0.0, 0.0}

[min, max] LO probe-frequency range of the full scan (MHz).

RfConfig::Sideband sideband = {RfConfig::UpperSideband}

Sideband to process when not in double-sideband mode.

bool doubleSideband = {false}

When true, process both sidebands simultaneously.

DeconvolutionMethod dcMethod = {Harmonic_Mean}

Method for combining overlapping FT bins across LO steps.