ClockManager
ClockManager owns the live Clock instances for the active hardware
loadout, maps each RfConfig::ClockType role to the physical clock object
that serves it, and mediates all configure and read operations that flow between
HardwareManager and the underlying oscillator hardware. It inherits from
SettingsStorage and persists the available clock output table so that the RF
configuration UI can present the correct options without querying live hardware.
ClockManager is created by HardwareManager and runs on the
HardwareManager thread. HardwareManager holds it via
std::unique_ptr<ClockManager> pu_clockManager and calls
setClocksFromHardwareManager() to hand off the current set of Clock
pointers after any hardware sync cycle. When the runtime hardware map changes,
HardwareManager calls updateClockManager() to rebuild the internal clock list
and re-emit routing signals. Direct calls from other threads must go through
HardwareManager’s queued connections — ClockManager’s public slots are not
safe to invoke directly across thread boundaries.
Primary collaborators: HardwareManager (owner, signal forwarder, and caller
of setClocks / configureClocks); RfConfig (source of the
RfConfig::ClockType enum used as the routing key and of the RfConfig::ClockFreq
descriptor passed in clock maps); HardwareLoadout (clock assignments are
part of the loadout’s RF section and are persisted per FTMW preset). The
Clock base class is a HardwareObject; ClockManager does not
own the Clock objects — it borrows raw pointers from HardwareManager.
Clock-routing model
Each RfConfig::ClockType role (UpLO, DownLO, AwgRef,
DRClock, DigRef, ComRef) is satisfied by exactly one
Clock object paired with an output index on that clock. A single
Clock device may expose multiple independent outputs (for example the
Valon 5009 has two independently tunable channels), so one physical device can
simultaneously satisfy several distinct roles — each on a different output port.
Internally, d_clockRoles maps every currently active ClockType to the
Clock that serves it. d_clockList holds the full set of
Clock objects that HardwareManager has provided; entries in
d_clockRoles are always a subset of that list. When configureClocks() is
called, the existing role map is cleared, each entry in the incoming
QHash<ClockType, ClockFreq> is resolved against d_clockList by hardware
key, the matching clock’s output is registered via Clock::addRole(), and the
multiplication factor is written to the output via Clock::setMultFactor().
Roles that were present before the call but absent from the new map receive a
clockHardwareUpdate(type, "", -1) signal so consumers know the role is no
longer active.
The multiplication factor in RfConfig::ClockFreq accounts for clocks whose
oscillator output frequency differs from the logical RF frequency by a fixed
multiply-or-divide relationship (e.g., a ×2 doubler stage). When the factor is
less than 1.0 the operation is RfConfig::Divide; otherwise it is
RfConfig::Multiply.
The available clock outputs are persisted in SettingsStorage under the
BC::Key::ClockManager::hwClocks array so that the RF configuration dialog can
populate its output selector without polling live hardware.
Configuration flow
The typical configure path during experiment preparation is:
HardwareManager::initializeExperiment()callsClockManager::prepareForExperiment(Experiment &exp).prepareForExperiment()extracts the clock map fromexp.ftmwConfig()->d_rfConfig.getClocks()and passes it toconfigureClocks().configureClocks()resolves eachClockType→Clock*binding, sets the multiplication factor, callsClock::setFrequency()on each affected output, and emitsclockHardwareUpdate()for every newly assigned role.After
configureClocks()returns,prepareForExperiment()writes the achieved frequencies back intoexpviaRfConfig::setCurrentClocks(getCurrentClocks()).When a mid-acquisition clock step requires a frequency change,
MainWindow::clockPromptcallsHardwareManager::setClocks()viaQMetaObject::invokeMethod;HardwareManagergates the FTMW digitizer during the transition and emitsHardwareManager::allClocksReady()when all clocks have settled. That signal is connected directly toAcquisitionManager::clockSettingsComplete, which gates progression to the next acquisition step.
Frequency reads (readClockFrequency(), readActiveClocks()) forward
directly to Clock::readFrequency(). Each Clock emits
frequencyUpdate(ClockType, double); setupClocks() connects that signal
to ClockManager::clockFrequencyUpdate, which HardwareManager then
re-emits to the rest of the system.
For the end-user view of clock assignment see RF Configuration and FTMW Presets.
API Reference
-
class ClockManager : public QObject, public SettingsStorage
Routes RF clock operations to the correct
Clockhardware objects by role.ClockManagerowns the role-to-hardware mapping for all clock sources in the active loadout. EachRfConfig::ClockTyperole (e.g.,UpLO,DownLO,AwgRef) is served by exactly oneClockobject on one of that clock’s numbered outputs. A single physical clock may serve multiple roles on different outputs simultaneously.ClockManageris created and owned byHardwareManager(pu_clockManager) and runs on the HardwareManager thread.HardwareManagerhands off the currentClock*pointers viasetClocksFromHardwareManager()after each hardware sync cycle, and callsupdateClockManager()whenever the set of available clocks changes. Direct invocation ofClockManagerslots from other threads is not safe; all cross-thread callers must go throughHardwareManager'squeued connections.See also
HardwareManager, RfConfig, Clock
Public Functions
-
explicit ClockManager(QObject *parent = nullptr)
Constructs a
ClockManagerwith an empty clock list.The manager reads its
SettingsStoragestate from theBC::Key::ClockManager::clockManagerkey. Clock objects must be supplied byHardwareManagerviasetClocksFromHardwareManager()before any configure or read operations are meaningful.- Parameters:
parent – Optional parent
QObject.
Public Slots
-
void readActiveClocks()
Reads the current hardware frequency for every active clock role.
Iterates over
d_clockRolesand callsClock::readFrequency()for each role whose clock reportsisConnected(). Each successful read causes the correspondingClockto emitfrequencyUpdate, which propagates throughclockFrequencyUpdate.Must be called on the HardwareManager thread.
-
QHash<RfConfig::ClockType, RfConfig::ClockFreq> getCurrentClocks()
Returns the current frequency descriptor for every active clock role.
Queries each entry in
d_clockRolesfor its hardware key, output index, multiplication factor, and live frequency, and assembles the results into a hash that can be written back into anRfConfigviaRfConfig::setCurrentClocks().Must be called on the HardwareManager thread.
- Returns:
A hash mapping each active
RfConfig::ClockTypeto itsRfConfig::ClockFreqdescriptor.
-
double setClockFrequency(RfConfig::ClockType t, double freqMHz)
Sets the frequency of the clock assigned to t.
Forwards the call to
Clock::setFrequency()on theClock*ind_clockRolesfor t. Logs a warning and returns-1.0if no clock is currently assigned to t.Must be called on the HardwareManager thread.
- Parameters:
t – The clock role to tune.
freqMHz – Desired output frequency in MHz.
- Returns:
Actual achieved frequency in MHz, or
-1.0on failure.
-
double readClockFrequency(RfConfig::ClockType t)
Reads the current frequency of the clock assigned to t.
Forwards the call to
Clock::readFrequency()on the assignedClock*. Logs a warning and returns-1.0if no clock is assigned to t.Must be called on the HardwareManager thread.
- Parameters:
t – The clock role to query.
- Returns:
Current frequency in MHz, or
-1.0on failure.
-
bool configureClocks(QHash<RfConfig::ClockType, RfConfig::ClockFreq> clocks)
Applies a complete clock configuration to hardware.
Clears all existing role assignments, then for each entry in clocks: resolves the hardware key to a
Clock*ind_clockList, registers the role on the correct output viaClock::addRole(), writes the multiplication factor viaClock::setMultFactor(), callsClock::setFrequency(), and emitsclockHardwareUpdate(). Roles present before the call but absent from clocks receive aclockHardwareUpdatewith an empty key and output-1to signal that they are no longer active.Returns
false(and logs an error) if a hardware key in clocks cannot be matched to a knownClock, if the requested output index is out of range, or if asetFrequencycall fails.Must be called on the HardwareManager thread.
- Parameters:
clocks – Desired role-to-frequency mapping as produced by
RfConfig::getClocks().- Returns:
trueif all roles were configured successfully;falseon the first error.
-
bool prepareForExperiment(Experiment &exp)
Configures clocks for an experiment and writes achieved frequencies back into the experiment object.
Returns
trueimmediately if the experiment does not have FTMW enabled. Otherwise callsconfigureClocks()with the clock map fromexp.ftmwConfig()->d_rfConfig.getClocks(), and on success callsRfConfig::setCurrentClocks(getCurrentClocks())to record the achieved frequencies in exp.Must be called on the HardwareManager thread.
- Parameters:
exp – The experiment to prepare. Modified in place with the actual clock frequencies after configuration.
- Returns:
trueon success or if FTMW is disabled;falseifconfigureClocks()fails.
-
void setClocksFromHardwareManager(const QVector<Clock*> &clocks)
Accepts a new set of
Clockpointers fromHardwareManager.Replaces
d_clockListwith clocks (which remain owned byHardwareManager) and callssetupClocks()to rebuild the role map and re-connectfrequencyUpdatesignals.Must be called on the HardwareManager thread.
- Parameters:
clocks – Ordered list of
Clock*objects currently in the hardware map.
-
void reconfigureFromRuntimeConfig()
Rebuilds the internal clock setup from the current
d_clockList.Called by
HardwareManagerwhen the hardware map changes aftersetClocksFromHardwareManager()has already been called. Equivalent to re-runningsetupClocks()on the existing pointer list.Must be called on the HardwareManager thread.
-
QVector<Clock*> getClockList() const
Returns the current list of
Clockpointers held by this manager.- Returns:
A copy of
d_clockListas supplied by the most recentsetClocksFromHardwareManager()call.
Signals
-
void clockFrequencyUpdate(RfConfig::ClockType type, double freqMHz)
Emitted when the frequency of a clock role changes.
Connected by
setupClocks()to eachClock::frequencyUpdatesignal and re-emitted byHardwareManagerto the rest of the system.- Parameters:
type – The logical clock role whose frequency has changed.
freqMHz – The new frequency in MHz.
-
void clockHardwareUpdate(RfConfig::ClockType type, const QString &hwKey, int output)
Emitted when the hardware binding for a clock role changes.
Fires once per role when
configureClocks()assigns or clears a role. If a role is removed (e.g., the clock map no longer includes it), this signal is emitted with an empty hwKey and output equal to-1.- Parameters:
type – The logical clock role that was reassigned.
hwKey – Hardware key of the newly assigned
Clock, or an empty string if the role is now unassigned.output – Output port index on the
Clockdevice, or-1if the role is unassigned.
Private Functions
-
void setupClocks()
Populates the
SettingsStorageoutput table and re-connects signals.Iterates
d_clockList, writes each clock’s outputs to theBC::Key::ClockManager::hwClocksarray inSettingsStorage, emitsclockHardwareUpdatefor roles that are already assigned on each clock, and connectsClock::frequencyUpdatetoClockManager::clockFrequencyUpdate. Called fromsetClocksFromHardwareManager()andreconfigureFromRuntimeConfig().
Private Members
-
QVector<Clock*> d_clockList
Ordered list of all
Clockobjects in the active loadout.Pointers are borrowed from
HardwareManager; lifetime is managed byHardwareManager. Updated bysetClocksFromHardwareManager().
-
QHash<RfConfig::ClockType, Clock*> d_clockRoles
Maps each active
RfConfig::ClockTyperole to theClock*that serves it.Rebuilt by
configureClocks(). A role is present in this hash only when a clock has been successfully assigned to it. Multiple roles may map to the sameClock*(each on a different output index).
-
explicit ClockManager(QObject *parent = nullptr)