TestSource: period and duty cycle for the P1 and P2 patterns

The sawtooth and square patterns had a fixed 1000 sample pulse width. Both
now take a period, and the square pattern a duty cycle, from the GUI, a
preset or the web API, with the period shown in seconds beside the sample
count.

A period of zero divides by zero in the sawtooth and wraps the unsigned
sample counter in the square pattern, giving a cycle of four billion
samples, so it is refused at every entrance: a minimum of one on the spin
box, a clamp when an older or hand edited preset is read, a clamp on the
web API, and a clamp in the worker for anything that reaches it another
way. The duty cycle is held to 0 to 100 the same way.

Also fixes the worker's timer. It is now parented to the worker so that it
moves to the worker thread with it, and it runs between startWork and
stopWork rather than from construction. TestSourceInput::stop() runs in
another thread, and Qt will not stop a timer from one, so the stop is
asked of the worker and waited for. The m_running flag it replaces is
gone, along with the test of it in every tick.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Jon Beniston
2026-09-11 19:42:07 +01:00
co-authored by Claude Opus 5
parent 37310ab984
commit d7bc4a94e4
9 changed files with 265 additions and 53 deletions
@@ -45,6 +45,8 @@ void TestSourceSettings::resetToDefaults()
m_iFactor = 0.0f;
m_qFactor = 0.0f;
m_phaseImbalance = 0.0f;
m_period = 1000;
m_dutyCycle = 50;
m_useReverseAPI = false;
m_reverseAPIAddress = "127.0.0.1";
m_reverseAPIPort = 8888;
@@ -75,6 +77,8 @@ QByteArray TestSourceSettings::serialize() const
s.writeString(19, m_reverseAPIAddress);
s.writeU32(20, m_reverseAPIPort);
s.writeU32(21, m_reverseAPIDeviceIndex);
s.writeS32(22, m_period);
s.writeS32(23, m_dutyCycle);
return s.final();
}
@@ -139,6 +143,11 @@ bool TestSourceSettings::deserialize(const QByteArray& data)
d.readU32(21, &utmp, 0);
m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp;
d.readS32(22, &m_period, 1000);
m_period = m_period < 1 ? 1 : m_period;
d.readS32(23, &m_dutyCycle, 50);
m_dutyCycle = m_dutyCycle < 0 ? 0 : m_dutyCycle > 100 ? 100 : m_dutyCycle;
return true;
}
else
@@ -201,6 +210,12 @@ void TestSourceSettings::applySettings(const QStringList& settingsKeys, const Te
if (settingsKeys.contains("phaseImbalance")) {
m_phaseImbalance = settings.m_phaseImbalance;
}
if (settingsKeys.contains("period")) {
m_period = settings.m_period;
}
if (settingsKeys.contains("dutyCycle")) {
m_dutyCycle = settings.m_dutyCycle;
}
if (settingsKeys.contains("useReverseAPI")) {
m_useReverseAPI = settings.m_useReverseAPI;
}
@@ -270,6 +285,12 @@ QString TestSourceSettings::getDebugString(const QStringList& settingsKeys, bool
if (settingsKeys.contains("phaseImbalance") || force) {
ostr << " m_phaseImbalance: " << m_phaseImbalance;
}
if (settingsKeys.contains("period") || force) {
ostr << " m_period: " << m_period;
}
if (settingsKeys.contains("dutyCycle") || force) {
ostr << " m_dutyCycle: " << m_dutyCycle;
}
if (settingsKeys.contains("useReverseAPI") || force) {
ostr << " m_useReverseAPI: " << m_useReverseAPI;
}